David Smerkous
Published © Apache-2.0

UDOO-RC-CAR

Xbox Controller Controlled RC-car

AdvancedFull instructions provided7,137
UDOO-RC-CAR

Things used in this project

Hardware components

UDOO QUAD
UDOO QUAD
Or Dual
×1
Solder
×1
Wire
×1
Rc car
×1
Arduino Motor Shield
×1

Software apps and online services

Arduino

Hand tools and fabrication machines

Scissors/Strippers
Soldering iron (generic)
Soldering iron (generic)
Monitor
Screw driver
XBOX Wireless Controller

Story

Read more

Code

DUE.ino

C/C++
This is the Arduino side of the UDOO which you'll need to upload
// All of these set the pin locations
int Direction = 12; //Set Direction of motor
int turnDir = 13;
int turnpower = 11;
int turnbrake = 8;
int Speed = 3; //Set speed of motor value of 0 to 255 analog
int Brake = 9; // Brake for motor usually unused so I always let sit 0
void setup(){
	// This defines the speed of arduino serial
	Serial.begin(115200);
	// These set modes of pins
	pinMode(Direction, OUTPUT);
	pinMode(Speed, OUTPUT);
	pinMode(Brake, OUTPUT);
	pinMode(turnbrake, OUTPUT);
	pinMode(turnDir, OUTPUT);
	pinMode(turnpower, OUTPUT);
	// Ininitalizes pins. Not moving
	digitalWrite(turnDir, LOW);
	digitalWrite(turnbrake, LOW);
	digitalWrite(Brake, LOW);
	analogWrite(Speed, 0);
	analogWrite(turnpower, 0);
// Wait for python script to start. Makes sure arudino doesn't overheat
while(Serial.read() != '1'){
	delay(100); 
}
}
void loop(){
	int a = Serial.read();
	// All of these are basically power levels. 0 is off, 6 is full power (forward)
	if(a == '0'){
		digitalWrite(Direction, LOW);
		analogWrite(Speed, 0);
		digitalWrite(turnDir, HIGH);
		analogWrite(turnpower, 0);
	}
	if(a == '1'){
		digitalWrite(Direction, LOW);
		analogWrite(Speed, 75);
	}
	if(a == '2'){
		digitalWrite(Direction, LOW);
		analogWrite(Speed, 155);
	}
	if(a == '3'){
		digitalWrite(Direction, HIGH);
		analogWrite(Speed, 55);
	}
	if(a == '4'){
		digitalWrite(Direction, HIGH);
		analogWrite(Speed, 145);
	}
	if(a == '5'){
		digitalWrite(Direction, HIGH);
		analogWrite(Speed, 255);
	}
	if(a == '6'){
		digitalWrite(Direction, LOW);
		analogWrite(Speed, 255);
	}
	// 7 and 8 are turning, 7 is left, 8 is right
	if(a == '7'){
		digitalWrite(turnDir, LOW);
		analogWrite(turnpower, 200);
	}
	if(a == '8'){
		digitalWrite(turnDir, HIGH);
		analogWrite(turnpower, 200);
	}
delay(15);
}

Robot.py

Python
Place this anywhere where it is located next to the XboxController.py and run it
from time import sleep
import XboxController
import serial
import pygame
due = serial.Serial("/dev/ttymxc3", 115200) # Points at serial port and sets speed
due.write('1') 
def myCallBack(controlId, value):
	if controlId == 1 and 0.3 < value < 0.5:
		due.write('1')
	elif controlId == 1 and 0.5 <= value < 0.7:
		due.write('2')
	elif controlId == 1 and -0.5 < value < -0.3:
		due.write('3')
	elif controlId == 1 and -0.7 <= value <-0.5:
		due.write('4')
	elif controlId == 1 and value <= -0.7:
		due.write('5')
	elif controlId == 1 and 0.7 <= value:
		due.write('6')
	elif controlId == 0 and 0.3 < value:
		due.write('7')
	elif controlId == 0 and -0.3 < value:
		due.write('8')
	else :
		due.write('0')
xboxCont = XboxController.XboxController(
	controllerCallBack = myCallBack,
	joystickNo = 0,
	deadzone = 0.1,
	scale = 1,
	invertYAxis = True)
xboxCont.start()
try:
	while True:
		myCallBack
		sleep(0.015)
catch KeyboardInterrupt:
	xboxCont.stop()

Terminal Commands(Copy paste into terminal)

Plain text
These commands are to start the camera as ip and run the xbox controller receiver
sudo modprobe v4l2loopback video_nr=7 card_label="UDOO Camera Module"
sudo gst-launch-0.10 mfw_v4lsrc ! ffmpegcolorspace ! v4l2sink device=/dev/video7

sudo service motion start

sudo xboxdrv --silent --detach-kernel-driver

XboxController.py

Python
Not officially made by me, look to the right to see who but place this next to the Robot.py file
import pygame
from pygame.locals import *
import os, sys
import threading
import time

"""
NOTES - pygame events and values
JOYAXISMOTION
event.axis              event.value
0 - x axis left thumb   (+1 is right, -1 is left)
1 - y axis left thumb   (+1 is down, -1 is up)
2 - x axis right thumb  (+1 is right, -1 is left)
3 - y axis right thumb  (+1 is down, -1 is up)
4 - right trigger
5 - left trigger
JOYBUTTONDOWN | JOYBUTTONUP
event.button
A = 0
B = 1
X = 2
Y = 3
LB = 4
RB = 5
BACK = 6
START = 7
XBOX = 8
LEFTTHUMB = 9
RIGHTTHUMB = 10
JOYHATMOTION
event.value
[0] - horizontal
[1] - vertival
[0].0 - middle
[0].-1 - left
[0].+1 - right
[1].0 - middle
[1].-1 - bottom
[1].+1 - top
"""
#Main class for reading the xbox controller values
class XboxController(threading.Thread):

    #internal ids for the xbox controls
    class XboxControls():
        LTHUMBX = 0
        LTHUMBY = 1
        RTHUMBX = 2
        RTHUMBY = 3
        RTRIGGER = 4
        LTRIGGER = 5
        A = 6
        B = 7
        X = 8
        Y = 9
        LB = 10
        RB = 11
        BACK = 12
        START = 13
        XBOX = 14
        LEFTTHUMB = 15
        RIGHTTHUMB = 16
        DPAD = 17

    #pygame axis constants for the analogue controls of the xbox controller
    class PyGameAxis():
        LTHUMBX = 0
        LTHUMBY = 1
        RTHUMBX = 2
        RTHUMBY = 3
        RTRIGGER = 4
        LTRIGGER = 5

    #pygame constants for the buttons of the xbox controller
    class PyGameButtons():
        A = 0
        B = 1
        X = 2
        Y = 3
        LB = 4
        RB = 5
        BACK = 6
        START = 7
        XBOX = 8
        LEFTTHUMB = 9
        RIGHTTHUMB = 10

    #map between pygame axis (analogue stick) ids and xbox control ids
    AXISCONTROLMAP = {PyGameAxis.LTHUMBX: XboxControls.LTHUMBX,
                      PyGameAxis.LTHUMBY: XboxControls.LTHUMBY,
                      PyGameAxis.RTHUMBX: XboxControls.RTHUMBX,
                      PyGameAxis.RTHUMBY: XboxControls.RTHUMBY}
    
    #map between pygame axis (trigger) ids and xbox control ids
    TRIGGERCONTROLMAP = {PyGameAxis.RTRIGGER: XboxControls.RTRIGGER,
                         PyGameAxis.LTRIGGER: XboxControls.LTRIGGER}

    #map between pygame buttons ids and xbox contorl ids
    BUTTONCONTROLMAP = {PyGameButtons.A: XboxControls.A,
                        PyGameButtons.B: XboxControls.B,
                        PyGameButtons.X: XboxControls.X,
                        PyGameButtons.Y: XboxControls.Y,
                        PyGameButtons.LB: XboxControls.LB,
                        PyGameButtons.RB: XboxControls.RB,
                        PyGameButtons.BACK: XboxControls.BACK,
                        PyGameButtons.START: XboxControls.START,
                        PyGameButtons.XBOX: XboxControls.XBOX,
                        PyGameButtons.LEFTTHUMB: XboxControls.LEFTTHUMB,
                        PyGameButtons.RIGHTTHUMB: XboxControls.RIGHTTHUMB}
                        
    #setup xbox controller class
    def __init__(self,
                 controllerCallBack = None,
                 joystickNo = 0,
                 deadzone = 0.1,
                 scale = 1,
                 invertYAxis = True):

        #setup threading
        threading.Thread.__init__(self)
        
        #persist values
        self.running = False
        self.controllerCallBack = controllerCallBack
        self.joystickNo = joystickNo
        self.lowerDeadzone = deadzone * -1
        self.upperDeadzone = deadzone
        self.scale = scale
        self.invertYAxis = invertYAxis
        self.controlCallbacks = {}

        #setup controller properties
        self.controlValues = {self.XboxControls.LTHUMBX:0,
                              self.XboxControls.LTHUMBY:0,
                              self.XboxControls.RTHUMBX:0,
                              self.XboxControls.RTHUMBY:0,
                              self.XboxControls.RTRIGGER:0,
                              self.XboxControls.LTRIGGER:0,
                              self.XboxControls.A:0,
                              self.XboxControls.B:0,
                              self.XboxControls.X:0,
                              self.XboxControls.Y:0,
                              self.XboxControls.LB:0,
                              self.XboxControls.RB:0,
                              self.XboxControls.BACK:0,
                              self.XboxControls.START:0,
                              self.XboxControls.XBOX:0,
                              self.XboxControls.LEFTTHUMB:0,
                              self.XboxControls.RIGHTTHUMB:0,
                              self.XboxControls.DPAD:(0,0)}

        #setup pygame
        self._setupPygame(joystickNo)

    #Create controller properties
    @property
    def LTHUMBX(self):
        return self.controlValues[self.XboxControls.LTHUMBX]

    @property
    def LTHUMBY(self):
        return self.controlValues[self.XboxControls.LTHUMBY]

    @property
    def RTHUMBX(self):
        return self.controlValues[self.XboxControls.RTHUMBX]

    @property
    def RTHUMBY(self):
        return self.controlValues[self.XboxControls.RTHUMBY]

    @property
    def RTRIGGER(self):
        return self.controlValues[self.XboxControls.RTRIGGER]

    @property
    def LTRIGGER(self):
        return self.controlValues[self.XboxControls.LTRIGGER]

    @property
    def A(self):
        return self.controlValues[self.XboxControls.A]

    @property
    def B(self):
        return self.controlValues[self.XboxControls.B]

    @property
    def X(self):
        return self.controlValues[self.XboxControls.X]

    @property
    def Y(self):
        return self.controlValues[self.XboxControls.Y]

    @property
    def LB(self):
        return self.controlValues[self.XboxControls.LB]

    @property
    def RB(self):
        return self.controlValues[self.XboxControls.RB]

    @property
    def BACK(self):
        return self.controlValues[self.XboxControls.BACK]

    @property
    def START(self):
        return self.controlValues[self.XboxControls.START]

    @property
    def XBOX(self):
        return self.controlValues[self.XboxControls.XBOX]

    @property
    def LEFTTHUMB(self):
        return self.controlValues[self.XboxControls.LEFTTHUMB]

    @property
    def RIGHTTHUMB(self):
        return self.controlValues[self.XboxControls.RIGHTTHUMB]

    @property
    def DPAD(self):
        return self.controlValues[self.XboxControls.DPAD]

    #setup pygame
    def _setupPygame(self, joystickNo):
        # set SDL to use the dummy NULL video driver, so it doesn't need a windowing system.
        os.environ["SDL_VIDEODRIVER"] = "dummy"
        # init pygame
        pygame.init()
        # create a 1x1 pixel screen, its not used so it doesnt matter
        screen = pygame.display.set_mode((1, 1))
        # init the joystick control
        pygame.joystick.init()
        # how many joysticks are there
        #print pygame.joystick.get_count()
        # get the first joystick
        joy = pygame.joystick.Joystick(joystickNo)
        # init that joystick
        joy.init()

    #called by the thread
    def run(self):
        self._start()

    #start the controller
    def _start(self):
        
        self.running = True
        
        #run until the controller is stopped
        while(self.running):
            #react to the pygame events that come from the xbox controller
            for event in pygame.event.get():

                #thumb sticks, trigger buttons                    
                if event.type == JOYAXISMOTION:
                    #is this axis on our xbox controller
                    if event.axis in self.AXISCONTROLMAP:
                        #is this a y axis
                        yAxis = True if (event.axis == self.PyGameAxis.LTHUMBY or event.axis == self.PyGameAxis.RTHUMBY) else False
                        #update the control value
                        self.updateControlValue(self.AXISCONTROLMAP[event.axis],
                                                self._sortOutAxisValue(event.value, yAxis))
                    #is this axis a trigger
                    if event.axis in self.TRIGGERCONTROLMAP:
                        #update the control value
                        self.updateControlValue(self.TRIGGERCONTROLMAP[event.axis],
                                                self._sortOutTriggerValue(event.value))
                        
                #d pad
                elif event.type == JOYHATMOTION:
                    #update control value
                    self.updateControlValue(self.XboxControls.DPAD, event.value)

                #button pressed and unpressed
                elif event.type == JOYBUTTONUP or event.type == JOYBUTTONDOWN:
                    #is this button on our xbox controller
                    if event.button in self.BUTTONCONTROLMAP:
                        #update control value
                        self.updateControlValue(self.BUTTONCONTROLMAP[event.button],
                                                self._sortOutButtonValue(event.type))
        
    #stops the controller
    def stop(self):
        self.running = False

    #updates a specific value in the control dictionary
    def updateControlValue(self, control, value):
        #if the value has changed update it and call the callbacks
        if self.controlValues[control] != value:
            self.controlValues[control] = value
            self.doCallBacks(control, value)
    
    #calls the call backs if necessary
    def doCallBacks(self, control, value):
        #call the general callback
        if self.controllerCallBack != None: self.controllerCallBack(control, value)

        #has a specific callback been setup?
        if control in self.controlCallbacks:
            self.controlCallbacks[control](value)
            
    #used to add a specific callback to a control
    def setupControlCallback(self, control, callbackFunction):
        # add callback to the dictionary
        self.controlCallbacks[control] = callbackFunction
                
    #scales the axis values, applies the deadzone
    def _sortOutAxisValue(self, value, yAxis = False):
        #invert yAxis
        if yAxis and self.invertYAxis: value = value * -1
        #scale the value
        value = value * self.scale
        #apply the deadzone
        if value < self.upperDeadzone and value > self.lowerDeadzone: value = 0
        return value

    #turns the trigger value into something sensible and scales it
    def _sortOutTriggerValue(self, value):
        #trigger goes -1 to 1 (-1 is off, 1 is full on, half is 0) - I want this to be 0 - 1
        value = max(0,(value + 1) / 2)
        #scale the value
        value = value * self.scale
        return value

    #turns the event type (up/down) into a value
    def _sortOutButtonValue(self, eventType):
        #if the button is down its 1, if the button is up its 0
        value = 1 if eventType == JOYBUTTONDOWN else 0
        return value
    
#tests
if __name__ == '__main__':

    #generic call back
    def controlCallBack(xboxControlId, value):
        print "Control Id = {}, Value = {}".format(xboxControlId, value)

    #specific callbacks for the left thumb (X & Y)
    def leftThumbX(xValue):
        print "LX {}".format(xValue)
    def leftThumbY(yValue):
        print "LY {}".format(yValue)

    #setup xbox controller, set out the deadzone and scale, also invert the Y Axis (for some reason in Pygame negative is up - wierd! 
    xboxCont = XboxController(controlCallBack, deadzone = 30, scale = 100, invertYAxis = True)

    #setup the left thumb (X & Y) callbacks
    xboxCont.setupControlCallback(xboxCont.XboxControls.LTHUMBX, leftThumbX)
    xboxCont.setupControlCallback(xboxCont.XboxControls.LTHUMBY, leftThumbY)

    try:
        #start the controller
        xboxCont.start()
        print "xbox controller running"
        while True:
            time.sleep(0.2)

    #Ctrl C
    except KeyboardInterrupt:
        print "User cancelled"
    
    #error        
    except:
        print "Unexpected error:", sys.exc_info()[0]
        raise
        
    finally:
        #stop the controller
        xboxCont.stop()

Updated Code

Here is the Github page where you can get the most updated code

Credits

David Smerkous

David Smerkous

8 projects • 87 followers
Currently an Artificial Intelligence Ph.D. student at Oregon State University. Long-time hardware enthusiast. Profile is old.

Comments