Introduction: Controlling Robot Over Bluetooth Using Xbox Steering Wheel

About: I'm an electrical/electronic engineering Graduate from the Dublin Institute of Technology and enjoy tinkering and building all sorts of projects.

So I'm guessing that I'm like a large amount of people on this site that are low key hoarders, keeping anything they could turn into a project or salvage for parts and this is an example of that. I have this old Mad Catz Steering wheel for the Xbox 360 and now that I have an Xbox one I don't really have any use for it anymore so decided to use it to control a toy car through Bluetooth. I would've liked to connect the USB to a Raspberry Pi but wasn't sure how to process the information from the USB so I just took it apart, identified the components I could use and designed my own circuit which would be controlled by an Arduino Uno. So in this post, I'll go through the steps on how I made this project. This is my first project on this site so forgive any mistakes.

Step 1: Components Used

1x Console Steering Wheel

1x Arduino Uno

1x Arduino Nano

1x Toy Car/Build a Robot

2x HM10/CC41-A Bluetooth Modules

1x 2n2551 NPN Transistor

2x 220 Resistors

2x 2.2k Resistors

4x 1K Resistors

1x 220uF Electrolytic Capacitor

1x H-Bridge Chip

1x Switch

2x 9V Battery

Copper Stripboard

Male Headers

Female Headers

Bare Copper Connecting Wire

Assorted Cables

The Steering Wheel doesn't have to be Mad Catz just any console steering wheel should work. For the toy car I had an old robot I used for a Robosumo competition and I'll be going through the circuitry and programming for it but not how to build it, in the competition my team mate was in charge of the physical build and details on this can be found at https://davidgreenan.wordpress.com/ I was in charge of circuitry and programming and if you're interested my OLD blog(current one found here) for the competition can be found here http://www.ronanbyrnesumo.wordpress.com. You could also use one of the many posts on this site to help you build a robot or you could use an old toy car and use that instead of building your own robot, it's up to you.

Step 2: Open Up the Wheel

Most Likely on the underside of the wheel you'll find the screws to open the wheel, remove them and make sure to not to lose them because we'll be closing it up later. When you open it up you'll see a whole mess of wires connected to a PCB, hopefully you'll be lucky like me and the board will have all of the wires labelled like in the pictures above. So because there was so many wires going to different parts of the board I grouped each set of wires by putting tape around them and then if I cut the wires from the jumpers so I could match the group of wires to the jumpers. I'd suggest leaving the jumpers in their socket so it's easy to match the wire to its use.

Once you've done that you can trace the wires from the wheel and the pedals, both use potentiometers, these wire just soldered to the board instead of being on a jumper cable but were still labelled for me. The wiper pin of the steering wheel and pedals were marked as follows: Steering Wheel LX, Right Pedal RTR and Left Pedal LTR. The pedals wire can be found on Jumper 6(J6) and the steering wheel isn't labelled with a jumper but we'll call it J7. On both jumpers, the positive and negative aren't labelled but they can be identified by the shape of the solder pad, square for positive and round for negative, for potentiometers the positive and negative don't really matter. After you identified the wires, make note and unsolder both sets of wires. For this project I only used one button and one LED so that is all I'm going to include in my circuit and code but feel free that add more buttons or LEDs if you want, I used the Xbox Button marked XE on (J4) and the player 2 LED mark LED2 on J5(the player 1 LED was blown). Below I'm going to list the buttons/LEDs going to the steering wheel.

J3:

  • Right Bumper = RB
  • Left Bumper = LB
  • Left Stick Button(LSB) = LT
  • Back Button = BA
  • Start Button = ST
  • D-Pad Right =DR
  • Right Stick Button(RSB) = RT
  • 3.3V = 3.3V

J4:

  • Y Button = Y
  • X Button = X
  • B button = B
  • A Button = A
  • Xbox Button = XE
  • D-Pad Up = UP
  • D-Pad Left = DL
  • D-Pad Down = DN for some reason?
  • Ground = GND

J5:

  • Unknown = MR
  • Unknown = ML
  • Vcc = Unconnected
  • Player 4 LED =LED4
  • Player 3 LED = LED3
  • Player 2 LED = LED2
  • Player 1 LED = LED1

J6:

  • Positive = (square pad)
  • Left Pedal = LTR
  • Right Pedal = RTR
  • Ground = (round pad)

J7(This isn't marked as J7 but anyway):

  • Negative = (round pad)
  • Steering Wheel = LX
  • Positive = (square pad)

Your labels and layout may differ but hopefully there're some things in common. I couldn't figure out what MR or ML were so if you know feel free to comment below. The two other jumpers (J1 and J2) on the board are for the USB and mic but I'm not using them in this project.

Step 3: Build Test Circuit and Code

From testing with my multimeter I found that the switches and buttons are connected to ground when pressed and that the LED wires are connected to the negative of the LEDs and the positive is connected to 3.3V on J3. From this I designed this circuit shown above, this is just the test circuit so I didn't include the Bluetooth module at this stage. The steering wheel ran of 3.3V but to to use the full resolution of the Arduinos ADC I powered the circuit using 5V.

First off I'm going to have the Xbox button to work as the power button, I have it connected to digital input D2 which being held high by a resistor, once the button is pressed the pin will be pull to ground. The Player 2 LED is connected to the 3.3V jumper which we could connect a digital output to but if we wanted to used any other LEDs this would turn them all on, so to leave that option open, I'm connecting a NPN transistor to D4 and this will work as an electronic switch once voltage is applied and of course a current limiting resistor must be placed before the base pin of the transistor and after or before the LED. And lastly the wipers of the steering wheel and pedals are connected to the analog inputs A0, A1 and A2, from reading these inputs we can measure how much the wheel is turned or how much the pedals are pressed.

I first tested this on a breadboard and it all worked fine so then I moved it to a copper strip board which you can see a few pictures above. A problem I had was the headers pin which would connect to the Arduino wouldn't connect without having to bend the pins because of the gap between the digital pins female header. So I unsoldered the pins I didn't need so it would fit properly, I also put new header pins on the side with the bent pins, this did make the circuit look messy but this isn't a beauty contest. On the rows where the two sides of the Arduino are connected, these rows will need to be broken in between so not to cause any problems, this can be done with a screw driver. When soldering the transistor, you have to be careful as it is sensitive to heat. Once done soldering go across each row with the continuity function on your multimeter to make sure there isn't any cross connections, if you find any run a small flat head screw driver between the rows if you cannot see where the cross is but if you can see solder crossing, unsolder it with a solder sucker or wick.

Now below is the code used to test the circuit:

//
// Test code for reading in the analog voltages from an Xbox Steering Wheel
// and displaying these inputs on the serial monitor
// Instructables Post: https://www.instructables.com/id/Controlling-Robot-Over-Bluetooth-Using-Xbox-Steeri/
// Blog: http://www.roboroblog.wordpress.com
// Created by Ronan Byrne
// Last Updated 21/08/2016
//


// Define Pins
int lPed = A2;
int rPed = A1;
int wheel = A0;
int Xb = 2;
int Xled = 4;


//Define Variables
boolean on = false;
String message = "";
float reverse, forward, angle;


void setup() {
  Serial.begin(9600);         // Begin Serial Monitor at 9600 baudrate
  Serial.println("Started");  // Print "Started"
  // Define INPUTS/OUTPUTS
  pinMode(lPed, INPUT);
  pinMode(rPed, INPUT);
  pinMode(wheel, INPUT);
  pinMode(Xb, INPUT);
  pinMode(Xled, OUTPUT);


  digitalWrite(Xled, LOW);    // Set Xbox LED to off
}


void loop() {
  if (digitalRead(Xb) == 0 && on == false) { // If power is off and button pressed, turn on
    on = true;
    digitalWrite(Xled, HIGH);  // Set Xbox LED to on
    Serial.println("Power On"); // Print "Power On"
    delay(200);                 // Delay 200ms to reduce chance of bouncing
  }
  else if (on == true) {
    // Arduino has a 10bit ADC so to convert the bits to voltage the formula is (input_bits/2^10-1)*voltage_range
    angle = (float(analogRead(wheel)) / 1023) * 5.0;  // Voltage from Steering Wheel
    forward = (float(analogRead(rPed)) / 1023) * 5.0; // Voltage from Right Pedal
    reverse = (float(analogRead(lPed)) / 1023) * 5.0; // Voltage from Left Pedal
    // Create a string of the voltages
    message = "Steering Wheel: " + String(angle) + ", Left Pedal: " + String(reverse)
              + ", Right Pedal: " + String(forward);
    Serial.println(message); // Print string


    if (digitalRead(Xb) == 0) { // If Xbox button is pressed, power off
      on = false;
      digitalWrite(Xled, LOW);  // Set Xbox Led off
      Serial.println("Power Off"); // Print "Power Off"
      delay(200);
    }
    delay(500);   // Delay 500ms between readings
  }
}

Step 4: Run Test

So if all went well this test should run no problem. The test is just to check that we can read in our three analog inputs and read our button press and illuminate the LED. Below in a video, I go through testing it. If something doesn't work like the steering voltage isn't changing, make sure your solder joints are good and the connections are correct.

Step 5: Bluetooth Module

The Bluetooth module I bought was the HM-10 but what I got was a clone which is actually the CC41-A, you can tell the difference by a missing crystal at the top left or powering it and see what its called over Bluetooth from searching on your phone. The main difference between the two is the "AT" commands, these are used to control the module and send it commands, the HM-10 has a lot more commands compared to the CC41-A and you'll have to send a carriage return and newline at the end of the command to the CC41-A and you don't have to with the HM-10. I'll talk more about that later but now onto connecting the module, mine had a breakout board and a regulator for the power pin(Vcc) so I could power it with 5 volts from the Arduino but if it doesn't, power it off the 3.3V. This module uses 3.3V logic so we can't connect the Arduino output straight to the RX pin on the module, if you have a logic level shifter I'd use it here but as I didn't, I just used two resistors. To get the 3.3 volts out of 5 you want two resistors with a 1:2 ratio so when we fill in the equation below we get 3.3:

Vout = Vin(1-R1/(R1+R2)) = 5(1-1k/(1k+2k))=3.3V

I used a 1k and 2.2k(didn't have a 2k but it still worked) and from the voltage divider just connect the top of the 2k resistor to the RX pin on the HM-10. Using higher value will mean less power loss but can cause problems at higher speeds. We don't need to do anything with the TX pin as the Arduino can read the 3.3V logic. The circuit diagram with the HM-10 module included is shown above. That's all to that, now we write a test code and setup the module.

#include<SoftwareSerial.h>

SoftwareSerial BTSerial(6, 7); //RX|TX
//SoftwareSerial BTSerial(11, 10); //RX|TX


void setup(){
  Serial.begin(9600);
  BTSerial.begin(9600); // default baud rate
  BTSerial.println("AT");
}


void loop(){
  //read from the HM-10 and print in the Serial
  if(BTSerial.available())
    Serial.write(BTSerial.read());
    
  //read from the Serial and print to the HM-10
  if(Serial.available())
    BTSerial.write(Serial.read());
}

Upload this code to your Arduino and open the serial monitor, at this point you will need to do different things depending on if you're using the HM-10 or the CC41-A, with the HM-10 you want to select the "No Line Ending" in the serial monitor and with the CC41-A select "Both NL & CR" which is new line and carriage return. Some commands are the same, you can see the list from the HM-10 here, and the CC41-A here. For both writing "AT" will return an "OK" so make sure you can get that back before proceeding, if you can't get the "OK" back check your connections and check the voltage on the voltage divider.

To display all of the commands with a brief description of what they do use the commands "AT+HELP" and a list of commands will print to the monitor, you can write the commands directly into the monitor and it'll be sent to the module. Below I'll go through the setup I used for the steering wheel which will act as the master and a few useful commands.

AT+ROLE1: Sets the module as master(has access to more commands than slave)

AT+ADDR: Returns the address of the module(not used in this project but could be useful)

AT+INQ: Searches for slave devices

AT+CONNx: Connect to device x from index list returned from AT+INQ

AT+CONx: Connect to device x where x is the hexadecimal address(The CC41-A doesn't has this commands so you have to use the AT+CONNx command)

AT+BAUDx: Sets baudrate to baudrate index x, the index numbers and corresponding baudrates can be seen in the AT commands link above. If x is left blank(ie AT+BAUD it'll return the current baudrate). When you change the baudrate you'll have to change the baudrates for the Serial.begin and BTSerial.begin to the new value. I set the baudrate to BAUD7(57600), I suggest not going any higher than this as I tried to set it at 115200 and ended up not being able to communicate with it.

Those are the main ones I used and for the actual setup you only need the AT+ROLE1 and AT+BAUDx, setting up the slave is the same but AT+ROLE0. If you get messages like this in the Serial monitor "þ˜ž˜æ€˜€", you probably have the monitor at the wrong baudrate.

As I haven't tested the above code with the HM-10 I can't say for sure it'll work.

Step 6: Building Bluetooth Car Circuit and Code + Final Steering Wheel Code

Now we can move onto the last step which is building the circuit for the car and programming it(I won't be showing how to build the car but you could hack a toy car or make your own for this project). The circuit I'm going to be building will only use two wheels which control the speed and turning but if your car has something like a servo motor controlling the steering, you could use that for turning rather than the back wheels like I am( You will have to change the code for this).

To control the motors I'll be using a driver or H-bridge chip(the SN75441ONE in this case), a pin diagram can be seen above and a table of the outputs and inputs. The reason for using this chip is that the Arduino can't supply enough current to the motors to get the most out of them so this chip lets us choose the direction of the motors but the current is drawn from an external supply rather than Arduino. All of the logic pins will be connected to a digital pin on the Arduino, once one of these logic pins is held high, the corresponding terminal pin will be held high at the external supply voltage and drive the motor. For this to work only one logic pin on each side can be high or no current will flow(ie if 1A is high, 2A most be low for the motor to move). VCC1 is connected to the 5V of the Arduino to power the logic pins and set which voltage they'll switch at and VCC2 is the external supply which powers the motors. To control the speed, you can supply a pulse width modulated voltage to the two enable pin(EN), EN1,2 controls the left motors speed and EN3,4 controls the right motors speed.

The full circuit diagram can be seen as well which has a few differences from the steering wheel circuit, as we're using batteries to power the circuit, we need to connect the batteries to Vin on the Arduino to power it(7V-12V), I'm using 9V or D batteries. We're also using an Arduino Nano rather than the Uno for its size, I had to solder male headers pins to the Nano and female header Pins to the board so I could remove the Nano when I wanted. Because these batteries don't have a high capacity, I'm using two in parallel to supply more current to the motors. There is a capacitor in parallel to the batteries as well, this is to stop voltage spikes caused by the motors when starting and stopping, these spikes could cause problems with the Arduino. And finally you just need to connect the driver chip and motors and that's the circuit done and like before, build this on a circuit board before putting it on strip board.

Now onto the code, we need to make a few adjustments to the steering wheel code so the following is how the steering wheel code works:

  • Wait for power button to be pressed
  • After Power button is pressed, start searching for the slave module. Flash the led every half second and search for slave module every 3 seconds.
  • Once found, connect, keep led on and start sending data every 10ms(100Hz)
  • If connection is lost, start searching again
  • If power button is pressed again, stop all processes.

The way the car code works is as follows:

  • Wait for master to connect
  • Once connected parse the data and calculate speed and direction
  • Adjust the speed depending on the state of the steering wheel

When searching for the slave I'm assuming there is not other devices visible to the master, on my phone I can see multiple devices but the master has only ever seen one, so when I see a device I connected to it. A much better way would been to see if a device was visible, try connect to the address of the slave using AT+CONx but as this function wasn't available in the CC41-A I used the other way I mentioned. I also found no command that I could use to manually disconnect from the slave, neither master nor slave respond to AT commands once connected so switching one of them off is the only option.

Below is the two codes:

//
// BLE_Wheel.ino
// This code reads in inputs from an Xbox steering wheel such as the 
// the steering wheel, pedals and switches. These inputs are being sent over 
// bluetooth to a robot start which reacts to these inputs
// Instructables Post: https://www.instructables.com/id/Controlling-Robot-Over-Bluetooth-Using-Xbox-Steeri/
// Blog: http://www.roboroblog.wordpress.com
// Created by Ronan Byrne
// Last Updated 14/09/2016
//


#include<SoftwareSerial.h>
// Include timer library which can be found
// here http://www.doctormonk.com/search?q=timer
#include<Timer.h>

SoftwareSerial BTSerial(6, 7); // RX|TX
Timer t;

// Define Pins
int lPed = A2;
int rPed = A1;
int wheel = A0;
int Xb = 2;
int Xled = 4;

//Define Variables
String inString = "";
boolean Connecting = false;
boolean Connected = false;
boolean on = false;
String message = "";
int count = 0;
float reverse, forward, angle;

void setup() {
  Serial.begin(57600);         // Begin Serial Monitor at 57600 baudrate
  BTSerial.begin(57600);

  // Define INPUTS/OUTPUTS
  pinMode(lPed, INPUT);
  pinMode(rPed, INPUT);
  pinMode(wheel, INPUT);
  pinMode(Xb, INPUT);
  pinMode(Xled, OUTPUT);

  digitalWrite(Xled, LOW);    // Set Xbox LED to off

  // The function takeReading runs every 100ms 
  t.every(100, takeReading); 
  BTSerial.println("AT");
  delay(500);
}

void loop() {
  t.update();// update the timer
  if (digitalRead(Xb) == 0) { // If power is off and button pressed, turn on
    if (on == false) {
      on = true;
      digitalWrite(Xled, HIGH);  // Set Xbox LED to on
      Serial.println("Power On"); // Print "Power On"
      delay(10); // Small delay between serial prints
      BTSerial.println("AT");
    }
    else {
      on = false;
      digitalWrite(Xled, LOW);  // Set Xbox Led off
      Serial.println("Power Off"); // Print "Power Off"
    }
    delay(200);  // Delay 200ms to reduce chance of bouncing
  }
  // If Recieving data from the module
  if (BTSerial.available()) {
    inString = BTSerial.readStringUntil('\n'); // stop reading current data when a new line is found
    inString.trim(); // Trim whitespace 
    //Serial.println(inString); // Print data to serial monitor
    // If slave found, connect
    if (inString == "Devices Found 1"){
      BTSerial.println("AT+CONN1");
      Connecting =true;
    }
    // If connected, start reading and sending data
    else if(inString == "+Connected  0x001583007773"){
      Connected = true;
      Connecting = false;
      digitalWrite(Xled,HIGH);
    }
    // If disconnect, stop reading and sending data
    else if (inString == "+Disconnected") {
      Connected = false;
      digitalWrite(Xled, LOW);
    }
  }


  //read from the Serial and print to the HM-10
  if (Serial.available()) {
    BTSerial.write(Serial.read());
  }
}


void takeReading() {
  if (on == true) {
    if (Connected == true) {
      // Arduino has a 10bit ADC so to convert the bits to voltage the formula is (input_bits/2^10-1)*voltage_range
      angle = (float(analogRead(wheel)) / 1023) * 5.0;  // Voltage from Steering Wheel
      forward = (float(analogRead(rPed)) / 1023) * 5.0; // Voltage from Right Pedal
      reverse = (float(analogRead(lPed)) / 1023) * 5.0; // Voltage from Left Pedal
      // Create a string of the voltages
      message =  String(angle) + "," + String(reverse)
                 + "," + String(forward);
      BTSerial.println(message); // Print string
    }
    else if(Connecting == false && Connected == false )   {
      if (count == 30) {// Search for slave every 3 seconds
        SearchSlave();
        count = 0;
      }
      // flash led every half second when not connected
      else if (count == 5 || count == 10 || count == 15 || count == 20 || count ==25) {
        boolean ledState = digitalRead(Xled);
        digitalWrite(Xled, !ledState);
      }
      count++;
    }
  }
}


void SearchSlave() {
    boolean ledState = digitalRead(Xled);
    digitalWrite(Xled, !ledState);
    BTSerial.println("AT+INQ");
}
//
// BLE_Car.ino
// This code control a robot car which is connected to a steering
// wheel over bluetooth which controls the output of the motors
// Instructables Post: https://www.instructables.com/id/Controlling-Robot-Over-Bluetooth-Using-Xbox-Steeri/
// Blog: http://www.roboroblog.wordpress.com
// Created By Ronan Byrne
// Last Updated 14/09/2016
//

#include<SoftwareSerial.h>
 
SoftwareSerial BTSerial(3, 2); //RX|TX

// Define Pins
int LMF = A5;
int LMR = A4;
int RMF = 7;
int RMR = 8;
int RS = 5;
int LS = 6;

// Define Variables
String message;
boolean Fdirection;
float wheel, rightPed, leftPed, Speed;

void setup() {
  Serial.begin(57600);
  BTSerial.begin(57600);

  pinMode(LMF, OUTPUT);
  pinMode(LMR, OUTPUT);
  pinMode(RMF, OUTPUT);
  pinMode(RMR, OUTPUT);
  pinMode(RS, OUTPUT);
  pinMode(LS, OUTPUT);
  Serial.println("AT");
  delay(1000);
  BTSerial.println("AT");

  // Set all outputs to off
  digitalWrite(LMF, LOW);
  digitalWrite(RMF, LOW);
  digitalWrite(LMR, LOW);
  digitalWrite(RMR, LOW);
  digitalWrite(RS, LOW);
  digitalWrite(LS, LOW);
}

void loop() {
  // Read from the HM-10
  if (BTSerial.available()) {
    // Parse the three floats
    wheel = BTSerial.parseFloat();
    leftPed = BTSerial.parseFloat();
    rightPed = BTSerial.parseFloat();
    message = String(wheel) + "," + String(leftPed) + "," +
              String(rightPed);
    //Serial.println(message);

    // Calculate the spped by the difference between the pedals
    Speed = ((rightPed - leftPed) / 5.0) * 255;
    if (Speed != 0) { // If speed is not zero, porcess data
      // If Speed is postive, the direction is forward
      if (Speed > 0.0) Fdirection = true;
      // if negative, the direction is reverse
      else {
        Fdirection = false;
        Speed = -Speed;
      }
      // Between 2.4V and 2.6V, the wheel is centered
      if (wheel > 2.6) {
        analogWrite(RS, Speed);
        analogWrite(LS, (1 - (wheel - 2.5) / 2.5)*Speed);
      }
      else if ( wheel < 2.4) {
        analogWrite(LS, Speed);
        analogWrite(RS, (wheel / 2.5)*Speed);
      }
      else {
        analogWrite(RS, Speed);
        analogWrite(LS, Speed);
      }


      if (Fdirection == true) {
        digitalWrite(LMF, HIGH);
        digitalWrite(RMF, HIGH);
        digitalWrite(LMR, LOW);
        digitalWrite(RMR, LOW);
      }
      else {
        digitalWrite(LMF, LOW);
        digitalWrite(RMF, LOW);
        digitalWrite(LMR, HIGH);
        digitalWrite(RMR, HIGH);
      }
    }
    else {
      digitalWrite(LMF, LOW);
      digitalWrite(RMF, LOW);
      digitalWrite(LMR, LOW);
      digitalWrite(RMR, LOW);
      analogWrite(RS, 0);
      analogWrite(LS, 0);
    }
  }
  //read from the Serial and print to the HM-10
  if (Serial.available()) {
    BTSerial.write(Serial.read());
  }
}

Step 7: Finished!!!

Now it's done, if you have any problems comment below and I'll see what I can do and if you're interested in other projects I've done you can visit my blog here.

Some improvements that could be made are using a car with servo controlled front wheel steering and motor-driven rear wheels, use an actual HM-10 so I could make use of the additional AT commands, add some extras to the car like a camera(you'd need to have the car controlled by something like a raspberry pi for this) or some other sensors and finally make use of the other buttons on the wheel. I'm interested in any variations you guys make to this so comment below what you did or even if you just got it working.