Introduction: BARC Jeep - an XBOX Controlled Power Wheels

About: I'm just a lonely robot trying to find love.
Make a power wheels jeep that is controlled by an XBOX controller. This particular build is using Power Wheels® Jeep® Hurricane with Monster Traction™ but should be usable across many different types of power wheels with small adjustments.

I wanted to do this project so I could drive my kids around. They are 2 and 3. We take the jeep out a few times a week. It's great for going to the park or just walking around the neighborhood. It works kind of like a stroller as far as containing and protecting them, but obviously way awesomer.

This project has also been built with expansion in mind. Because the motor controller boards have feedback, and there's plenty of room left on the Arduino microcontroller, it will be easy to add things such as proximity sensors and driver profiles. That way, when I give it to my little boy to drive himself, I can set the driver profile to be gentler as well as activate proximity sensors so he can't ram a tree.

Also, in essence this entire build is a fairly simple way to get a robot off the ground. My project was particular to the power wheels because I'd been dreaming of sitting on my porch with my controller and driving my kids in the yard, but really the setup could apply to anything with motor driven wheels and a steerable front end. (motor size etc taken into consideration of course).

I hope you enjoy reading about my project as much as I did making it. And if you do decide to tackle this Instructable and build your own, please contact me for any questions. I want to keep improving this so as many people can take that old Power Wheels and make it into the coolest thing on the block.

Now let' s make it!
Things you will need: Tools / Supplies:
     * Soldering Iron (i like this one)
     * Solder
     * Wire Strippers
     * A saw or cutting wheel to cut metal (i used an end grinder with a cutting wheel)
     * Wrenches / Screwdrivers
     * Drill - (a drill press is good for the steering plates, but a drill will work)

Overview:
  • Connect the Arduino to the USB Host shield, then connect that to the Wireless gaming receiver.
  • Drive the linear actuator with the Pololu Jrk Motor Controller
  • Drive the motors already in the Power Wheels with the Pololu Simple High-Power Motor Controller
  • Connect both motor controllers to the Arduino so they are controlled by the Xbox controller.
  • Remove the steering mechanism currently in the Power Wheels
  • Build a custom steering mechanism that is controlled with the linear actuator.
Sounds easy, right!

Step 1: Get the Xbox Controller Communicating to the Arduino

The first thing is get the Xbox controller communicating with the Arduino via the USB Host Shield. All of the pics included in this Instructable show an Arduino Mega, but in reality, an Uno is all that is needed for the USB Host Shield and only 2 digital outs.

Plug the USB Host Shield onto the Arduino. For now, power the Arduino with you're computer. Next plug the Xbox 360™ Wireless Gaming Receiver for Windows®. Once these connections are made load code shown below into the Arduino which allows the Arduino to perform steering operations based on input from the controller.

There's plenty of notation to help understand what's going on.

Arduino Code
// *****************************
// *    RC Power Wheels Jeep   *
// *****************************

/*

OVERVIEW
A 12v dual-motor Power Wheels Jeep Hurricane will have drive
and steering remotely-controlled via Arduino.

HARDWARE
Power Wheels Jeep Hurricane
Arduino UNO R3
Circuits@Home USB Host Shield 2.0          http://www.circuitsathome.com/products-page/arduino-shields/usb-host-shield-2-0-for-arduino
Pololu Simple Motor Controller 18v25       http://www.pololu.com/catalog/product/1381/resources
Pololu JRK Motor Controller 12v12          http://www.pololu.com/catalog/product/1393/resources
Pololu Generic Linear Actuator 4" .6"/s    http://www.pololu.com/catalog/product/2333
Xbox 360 Wireless USB Adapter
Xbox 360 Wireless Controller

HARDWARE CONFIGURATION

* +-------+         +---------------+  USB    +-----------------+
* |Arduino+-------->|USB Host Shield+-------->|XBOX USB Receiver|****/>    </****XBOX Wireless Controller
* +-+-----+         +---------------+         +-----------------+                 
* |
* | Tx (TTL Serial)
* |
* |------------------|
* |                  |
* v Rx               v Rx
* +----------+      +----------+
* |Pololu SMC+      |Pololu Jrk|
* +----------+      +----------+
* Master            Slave
* +                 +
* |                 |
* |                 |
* v                 v
* +--------+        +--------+
* |Dual 12v|        |Linear  |
* | Motors |        |Actuator|
* +--------+        +--------+
*
* NOTES
* Outdoors with line-of-sight, the Wireless Xbox Controller has an awesome range. The max range
* has not been fully tested, but I've gone over 50ft without any noticeable loss in signal.
*
* Everything sits on board the Jeep. All TTL Serial connections are hard-wired into the Arduino
* and motor controllers. The Xbox Receiver is attached to a metal pole antenna-style.
*
* The current linear actuator may be too slow. It can move 110lbs of force but at .6"/s. There
* is a faster model that does 22lbs @ 1.5"/s http://www.pololu.com/catalog/product/2345
*
*/

// *****Includes for the Circuits@Home USB Shield *****
#include <XBOXRECV.h>  // Xbox 360 Wireless Receiver

//Create USB instance? USB SHIELD
USB Usb;

// Create an instance of the Xbox Receiver inputs called XboxRCV
XBOXRECV XboxRCV(&Usb);  // USB SHIELD

// These next constants are bytes part of the Pololu Protocol
const byte pololuCommandByte = 170;
const byte smcDeviceNumber = 13;
const byte smcSpeedDataByte3 = 0;
const byte smcFWDbyte = 5;
const byte smcREVbyte = 6;
const byte jrkDeviceNumber = 11;

char smcSpeed;  // Final speed
long int leftStickX; // Xbox Left Analog Stick value


void setup(){
  Serial.begin(9600);    // Serial baud rate to 9600

    // Halt program until shield is connected
  // USB.Init will return -1 for shield disconnected and 0 if connected
  if (Usb.Init() == -1) {    // If USB shield did not initialise for whatever reason...
    while(1); //halt as long as shield is reported disconnected
  }

}


void loop(){
  Usb.Task();
  // Let's process the Xbox input
  if(XboxRCV.Xbox360Connected[0]) {

    // START button sends exitSafeStart command to SMC
    if(XboxRCV.getButtonClick(START,0)){
      // *******************************
      // *        exitSafeStart        *
      // *******************************
      // Required to allow motors connected to SMC to move
      // Must be called when controller restarts and after any error
      // Pololu Protocol: 0xAA (170) | device number | 0x03 (3)
      Serial.write(pololuCommandByte);
      Serial.write(smcDeviceNumber);
      Serial.write(3);
    }

    /* The Xbox triggers provide values from 0 - 255. The SMC will accept a low
     resolution speed value as a percentage, 0% - 100% (High resolution is
     a 2-byte int). The next two lines maps the controller to output a
     negative value for L2 (Reverse) and positive for R2 (Forward). These two
     values are then summed to provide the final speed and direction. This is
     so that both triggers can be held simultaneosly without causing the values
     to oscillate between Forward and Reverse
     */
    char XboxL2 = map((XboxRCV.getButtonPress(L2,0)), 0, 255, 0, -100);
    char XboxR2 = map((XboxRCV.getButtonPress(R2,0)), 0, 255, 0, 100);


    // Sum the mapped inputs together to give a final speed and direction
    smcSpeed = XboxL2 + XboxR2;



    /* The sample code for the Xbox controller gave a deadzone of -7500 to 7500.
     This code maintains that dead zone for now (I would like to make it
     adjustable while the sketch is running). */

    leftStickX = map(XboxRCV.getAnalogHat(LeftHatX,0), -32768, 32767, 0, 3880);  // Analog stick moved
    // Set the dead band in the left analog stick. Would like this to be adjustable
    if ((leftStickX >= 1500) && (leftStickX <= 1983)){
      leftStickX = 1400;
    }
  }

  // If no triggers/sticks are moving, then center and zero
  else {
    leftStickX = 1400;
    smcSpeed = 0;
  }


  // ************* RESERVED "HEARTBEAT" **********
  // "Heartbeat" will send a serial command every x seconds as a "keep-alive" to the SMC and JRK
  // controllers. It will also prevent duplicate commands from flooding the serial buffer (ideal
  // for Xbee implementation).


  // *******************************
  // *      SEND SERIAL COMMANDS   *
  // *******************************
  /* Reserved for serial commands sent to motor controllers to adjust
   option parameters. Also to process the response from those
   commands if applicable. */



  // THIS SECTION SENDS THE SPEED AND DIRECTION COMMANDS TO THE SMC
  // *******************************
  // *        setMotorSpeed        *
  // *******************************
  /*
  http://www.pololu.com/docs/0J44/6.2.1

   The Pololu SMC can use a full resolution speed value (-3200 to 3200), however, this is not needed
   (yet) since the Xbox controller analog triggers only output 0 to 255. The below tables are copied
   straight from the manual linked above. We'll be using a low resolution speed value expressed in
   percentage (0 to 100).

   "Alternate Interpretation: The allowed values for the second speed data byte are 0–100,
   so you can ignore the first speed data byte (always set it to 0), and consider the
   second data byte to simply be the speed percentage. For example, to drive the motor at
   53% speed, you would use byte1=0 and byte2=53."

   Motor Forward
                    Command Byte Data Byte 1 Data Byte 2 Data Byte 3 Data Byte 4
   Pololu Alternate Use 0xAA (170) device number 0x05 (5) 0 (always)      speed %


   Motor Reverse (data byte 2 changes)
                    Command Byte Data Byte 1 Data Byte 2 Data Byte 3 Data Byte 4
   Pololu Alternate Use 0xAA (170) device number 0x06 (6) 0 (always)      speed %

   */
  // smcSpeed should be a number from -100 to 100

    // First send the Pololu SMC command byte
  Serial.write(pololuCommandByte);

  // Next, send the SMC device number
  Serial.write(smcDeviceNumber);

  // Here, let's determine the speed and direction.
  if (smcSpeed < 0)  // Let's reverse since the speed is negative
  {
    Serial.write(6);  // motor reverse command
    smcSpeed = -smcSpeed;  // make smcSpeed positive b/c the command can only read positive numbers
  }
  else
  {
    Serial.write(5);  // motor forward command
  }

  Serial.write(smcSpeedDataByte3);  // Always zero (for now) because of the protocol being used

  // Now let's send the actual speed
  Serial.write(smcSpeed);
  delay(1);  // For stability


  // NEXT SECTION SENDS THE POSITION TO THE LINEAR ACTUATOR VIA THE JRK
  // *******************************
  // *          setJRKPos          *
  // *******************************
  /* http://www.pololu.com/docs/0J38/4.e
   Pololu protocol, hex: 0xAA, device number, 0x40 + target low 5 bits, target high 7 bits
   Here is some example C code that will generate the correct serial bytes,
   given an integer “target" that holds the desired target (0 - 4095) and an array called serialBytes:

   1 serialBytes[0] = 0xC0 + (target & 0x1F); // Command byte holds the lower 5 bits of target.
   2 serialBytes[1] = (target >> 5) & 0x7F;   // Data byte holds the upper 7 bits of target. */

  Serial.write(pololuCommandByte);
  Serial.write(jrkDeviceNumber);
  Serial.write(0x40 + (leftStickX & 0x1F));
  Serial.write((leftStickX >> 5) & 0x7F);

  delay(1);  // For stability


}

Step 2: Load Profiles Onto the Motor Controllers

Each motor controller requires a specific profile. These will specify the power and speed at which the motors are engaged when they receive a signal from the Arduino. You can copy them from below, or you can get the settings from my github entry.

The Pololu USB Software Development Kit is needed to program these controllers. Here's a link on how to download and use that SDK so you can use the code below. The SDK works for both motor controllers.

Here is the profile for the Pololu Simple High-Power Motor Controller 18v25:


<!--Pololu Simple Motor Controller settings file. http://www.pololu.com/docs/0J44-->
<!--Created on: 2013-08-31 09:45:35-->
<!--Device model: Pololu Simple High-Power Motor Controller 18v25-->
<!--Device serial number: 3200-7106-3147-3331-1823-1843-->
<!--Device firmware version: 1.04-->
<SmcSettings version="1">
<InputMode>SerialUsb</InputMode>
<MixingMode>None</MixingMode>
<DisableSafeStart>false</DisableSafeStart>
<IgnorePotDisconnect>false</IgnorePotDisconnect>
<IgnoreErrLineHigh>false</IgnoreErrLineHigh>
<NeverSuspend>false</NeverSuspend>
<TempLimitGradual>true</TempLimitGradual>
<OverTemp Min="700" Max="800" />
<LowVinShutoffTimeout>250</LowVinShutoffTimeout>
<LowVinShutoffMv>5500</LowVinShutoffMv>
<LowVinStartupMv>6000</LowVinStartupMv>
<HighVinShutoffMv>25000</HighVinShutoffMv>
<SerialMode>Binary</SerialMode>
<SerialDeviceNumber>13</SerialDeviceNumber>
<CommandTimeout>0</CommandTimeout>
<CrcMode>Disabled</CrcMode>
<UartResponseDelay>false</UartResponseDelay>
<UseFixedBaudRate>true</UseFixedBaudRate>
<FixedBaudRate>9600</FixedBaudRate>
<!--Input Settings-->
<Rc1>
<AlternateUse>None</AlternateUse>
<Invert>false</Invert>
<ScalingDegree>0</ScalingDegree>
<Error Min="2000" Max="10000" />
<Input Min="4000" Max="8000" />
<InputNeutral Min="5900" Max="6100" />
</Rc1>
<Rc2>
<AlternateUse>None</AlternateUse>
<Invert>false</Invert>
<ScalingDegree>0</ScalingDegree>
<Error Min="2000" Max="10000" />
<Input Min="4000" Max="8000" />
<InputNeutral Min="5900" Max="6100" />
</Rc2>
<Analog1>
<AlternateUse>None</AlternateUse>
<PinMode>Floating</PinMode>
<Invert>false</Invert>
<ScalingDegree>0</ScalingDegree>
<Error Min="0" Max="4095" />
<Input Min="40" Max="4055" />
<InputNeutral Min="2015" Max="2080" />
</Analog1>
<Analog2>
<AlternateUse>None</AlternateUse>
<PinMode>Floating</PinMode>
<Invert>false</Invert>
<ScalingDegree>0</ScalingDegree>
<Error Min="0" Max="4095" />
<Input Min="40" Max="4055" />
<InputNeutral Min="2015" Max="2080" />
</Analog2>
<!--Motor Settings-->
<PwmPeriodFactor>0</PwmPeriodFactor>
<MotorInvert>true</MotorInvert>
<SpeedZeroBrakeAmount>16</SpeedZeroBrakeAmount>
<SpeedUpdatePeriod>100</SpeedUpdatePeriod>
<ForwardLimits>
<MaxSpeed>3200</MaxSpeed>
<MaxAcceleration>100</MaxAcceleration>
<MaxDeceleration>200</MaxDeceleration>
<BrakeDuration>2000</BrakeDuration>
<StartingSpeed>0</StartingSpeed>
</ForwardLimits>
<ReverseLimits>
<MaxSpeed>3200</MaxSpeed>
<MaxAcceleration>100</MaxAcceleration>
<MaxDeceleration>200</MaxDeceleration>
<BrakeDuration>2000</BrakeDuration>
<StartingSpeed>0</StartingSpeed>
</ReverseLimits>
<!--Advanced Settings-->
<PulsePeriod Min="9" Max="100" />
<RcTimeout>500</RcTimeout>
<ConsecGoodPulses>2</ConsecGoodPulses>
<VinMultiplierOffset>0</VinMultiplierOffset>
</SmcSettings>
*/

And here is the profile for the Pololu Jrk 12v12 USB Motor Controller with Feedback:
INITIALIZED 0
INPUT_MODE SERIAL
INPUT_MINIMUM 0
INPUT_MAXIMUM 4095
OUTPUT_MINIMUM 0
OUTPUT_NEUTRAL 2048
OUTPUT_MAXIMUM 4095
INPUT_INVERT 0
INPUT_SCALING_DEGREE 0
INPUT_POWER_WITH_AUX 0
INPUT_ANALOG_SAMPLES_EXPONENT 5
INPUT_DISCONNECT_MINIMUM 0
INPUT_DISCONNECT_MAXIMUM 4095
INPUT_NEUTRAL_MAXIMUM 2049
INPUT_NEUTRAL_MINIMUM 2046
SERIAL_MODE UART_FIXED_BAUD_RATE
SERIAL_FIXED_BAUD_RATE 9600
SERIAL_TIMEOUT 2
SERIAL_ENABLE_CRC 0
SERIAL_NEVER_SUSPEND 0
SERIAL_DEVICE_NUMBER 11
FEEDBACK_MODE ANALOG
FEEDBACK_MINIMUM 1450
FEEDBACK_MAXIMUM 3880
FEEDBACK_INVERT 0
FEEDBACK_POWER_WITH_AUX 1
FEEDBACK_DEAD_ZONE 12
FEEDBACK_ANALOG_SAMPLES_EXPONENT 4
FEEDBACK_DISCONNECT_MINIMUM 40
FEEDBACK_DISCONNECT_MAXIMUM 3900
PROPORTIONAL_MULTIPLIER 35
PROPORTIONAL_EXPONENT 0
INTEGRAL_MULTIPLIER 0
INTEGRAL_EXPONENT 0
DERIVATIVE_MULTIPLIER 60
DERIVATIVE_EXPONENT 0
PID_PERIOD 10
PID_INTEGRAL_LIMIT 1000
PID_RESET_INTEGRAL 1
MOTOR_PWM_FREQUENCY 0
MOTOR_INVERT 1
MOTOR_MAX_DUTY_CYCLE_WHILE_FEEDBACK_OUT_OF_RANGE 600
MOTOR_MAX_ACCELERATION_FORWARD 600
MOTOR_MAX_ACCELERATION_REVERSE 600
MOTOR_MAX_DUTY_CYCLE_FORWARD 600
MOTOR_MAX_DUTY_CYCLE_REVERSE 600
MOTOR_MAX_CURRENT_FORWARD 0
MOTOR_MAX_CURRENT_REVERSE 0
MOTOR_CURRENT_CALIBRATION_FORWARD 37
MOTOR_CURRENT_CALIBRATION_REVERSE 37
MOTOR_BRAKE_DURATION_FORWARD 0
MOTOR_BRAKE_DURATION_REVERSE 0
MOTOR_COAST_WHEN_OFF 1
ERROR_ENABLE 102
ERROR_LATCH 98
*/

Step 3: Build the Breakout Board for the Motor Controllers

The goal here is to give our two motor controller boards a place to live, share power, and receive signal from the Arduino.

I added headers to a perfboard then soldered the connections for a breakout board. I made each pin accessible for future expansion, but the diagram above gives you the basic connections needed for the build in this Instructable. Here's a link to how to make a perfboard circuit.

The connections in the wiring diagram are:
RED = hacked USB power for the Arduino sourced from the 5v regulated of the JRK board. (this is not ideal, but I wasn't able to build a seperate power supply for the Arduino yet).
BLACK = All the grounds need to be tied together.
BLUE = Serial communication to both motor controller boards.
GREEN = Tie the errors together so if one motorcontroller makes an error they both stop.


Step 4: Placing Motor Controller and Connecting to the Arduino

Now that we've built a place for the motor controllers to live and receive signal, plug them into the Arduino as shown om the diagram on the previous step, then connect them to the Arduino.

Step 5: Adding Power to the System

Everyone needs power now.

To get power to the Arduino, source it from regulated 5v pin on the JRK Motor-Controller using a USB power cord hacked with a jumper header. The connection is shown on the diagram in Step 3. Using the breakout board created earlier, connect the two motor controllers then plug the Simple Motor-Controller into the Power Wheels battery and watch it all light up. To see the standard connections to motors and power see the information on the links for each motor controller.

Step 6: Creating the Steering Mechanism and Connecting It the Actuator

Remove the wheels and existing steering apparatus. Take the sheet of aluminum from the hardware store and cut it approximately to the dimensions provided in the blueprints. These metal parts don't have to be exact, but the diagrams gives a good idea. The idea is to sandwich the existing plastic block with top squarish metal plate and the lower triangle-like half. The triangles with box-ends should match with the inside edge of the wheel articulation arm. Bolt the triangles to the body of the wheel articulation arm using the the existing bolt holes to make your hole pattern in the metal.

Once all the metal is attached to the jeep, next connect the two triangles using the shelf track. The shelf track is a good choice because the slotted holes make for quick adjustments during trial and error. See the pictures above for approximate placement. Using two pieces of shelf track is recommended for stability and responsiveness.

Now drill a hole and bolt the Actuator to the alumuminum square tube that runs across the front of the jeep's underside. Put it as far to one side as possible without interfering with other systems. Also, keep in mind the arm must be able to turn freely, so offset it a sufficient distance from the square tube and the rest of the chassis to allow for free movement.

Next attach the actuator rod to the forward most piece of shelf track. The actuator should be placed at approximately the center of its throw while the wheels are straight forward. This actuator has a throw of 4 inches and has maximum power toward the center of its throw. In other words, it works best through the middle rather than at either end.

Lock nuts with the plastic inserts or some other means of securing, such as locktite, are recommended once the prototyping is finished to keep connections from vibrating apart. But a lot of trial and error will be spent getting all the parts to line up and act just right during testing, so keep that in mind.

Step 7: Run Actuator Control Wire and Place Electronics

The final step is to run the actuator control wire and place the electronics. The easiest way to get a wire to the actuator is to fish it through the dashboard after removing the windshield. Once that's accomplished, place the assembled electronics in the trunk, connect the power wheels motors to the Simple Motor-Controller, connect the actuator to the JRK Motor-Controller, make sure all other connections are secure, then connect the main battery power to the Simple Motor-Controller.

Remember, the code requires you to hit the start button for safety. The left joystick controls steering, the left trigger is forward, and the right trigger is reverse.

Enjoy your new giant RC!!!

Microcontroller Contest

Second Prize in the
Microcontroller Contest