Introduction: Computer Controlled RC Car With Two Arduinos

Required Parts: This project requires two Arduino boards which can be bought from http://www.adafruit.com/category/17
This project uses two Arduino Uno compatible boards.

An L293D or other Dual H-Bridge Motor Driver chip will be needed to drive the motors of the RC car. This can be found here: http://www.adafruit.com/products/807

Finally, this project requires the RF transmitter (https://www.sparkfun.com/products/retired/8945) and receiver (ttps://www.sparkfun.com/products/retired/8945) which will used for communication between the Arduinos.

A breadboard to prototype your circuit and a prototype board is also needed to solder everything together in the end. This can be bought from an electronics store like Jaycar Electronics.

Required Knowledge: A basic knowledge of electronics is required for this tutorial. If you have never worked with electronics take a look at this tutorial (at least up to lesson 4): http://www.ladyada.net/learn/arduino/
Knowing how to solder will also help with the final steps: https://learn.sparkfun.com/tutorials/how-to-solder...
This tutorial also assumes that you have a basic knowledge of the Arduino Uno and know how to program and upload a basic sketch. For the basics of Arduino, check out the Adafruit Arduino basics lessons at: http://learn.adafruit.com/lesson-0-getting-started/the-lessons.
A basic knowledge of Java is also assumed. You should know how to set up Java on your computer and create a basic Java program. This tutorial uses the Eclipse IDE. For the basics of programming Java and using Eclipse, check out this tutorial: http://eclipsetutorial.sourceforge.net/totalbeginner.html

Tutorial Structure: This tutorial will be split up into 3 parts:
  • Sending Arduino
  • Recieving Arduino
  • Computer program

Step 1: Sending Arduino: Wiring and Building.

Pick one of your Arduinos to be the sending one and the other to be the receiver. The layout for the sending Arduino is simple. We are using a breadboard to connect the RF Transmitter to the Arduino. The four pins on the transmitter are as follows: Ground, Data in, 5V in, Antenna.
Place the transmitter in the breadboard and wire the Ground and 5V pins to the corresponding Ground and 5V on the Ardunio. The data in pin should be connected to pin 12 on the Arduino. The antenna is optional but to gain more range, you can simply attach a long wire and leave it hanging.

Step 2: Sending Arduino: the Code.

Coding the Sending Arduino is also simple. For both this and the reciever, you need to download the Virtual Wire library from http://www.airspayce.com/mikem/arduino/
This tutorial uses version 1.20 (download link: http://www.airspayce.com/mikem/arduino/) any version should be compatible.

At the top of the sketch put:
#include <VirtualWire.h> //include the required library
In the setup function put:
void setup()
{
  //Begin the serial connection
  Serial.begin(9600);
  while(!Serial); //Wait till serial connection is ready
  Serial.println("Serial ready"); //Debugging
  //Initialize the IO and ISR
  vw_setup(2000); // Bits per sec
}
This code simply initializes the serial connection and Virtual Wire.
The Loop function should look like this:
void loop()
{
  
  //If serial input is available
  if(Serial.available())
  {
    char input = Serial.read(); //Read input
    send(&input); //Send input via RF transmitter
  } else {  
  }
}
This reads an input character and sends it to the send function.
Finally the send function should be created to look like this:
void send (char *message)
{
  vw_send((uint8_t *)message, strlen(message)); //Send the message
  vw_wait_tx(); // Wait until the whole message is gone
}
The send function takes a character and sends it using the Virtual Wire library.
This is all that's required for the sending Arduino.

You can download the complete sketch from here: Simple_send.ino

Step 3: Receiving Arduino: Wiring and Building.

Wiring the receiving Arduino is a little more complicated.
It requires both the RF receiver and the L293D chip.

The RF receiver:
The pins on the RF receiver labelled GND are ground. DATA OUT is where the received signal will be sent out. VDD is power for the chip and ANT is for the optional antenna.
-Wiring:
The RF receiver is connected separately from the L293D chip. The ground goes to one of the ground ports on the Arduino. The power in goes to the Arduino's 5V output and the DATA OUT should be connected to pin 13 on the Arduino. The antenna can be left loose and later connected to the RC car's antenna.

The L293D chip:
The pins on the L293D chip are shown on the diagram above. EN1 and EN2 are the motor enable pins. They act as a master on/off switch for each motor. IN1,IN2,IN3 and IN4 are the inputs that control the directions of each motor. If IN1 is powered and IN2 is not, the motor on OUT1 and OUT2 will turn one way. Likewise if the inputs are flipped, so too are the outputs and the motor will turn the other way. 0V is ground and +V is power for the chip. +Vmotor is used as a separate power source for the motors.
-Wiring:
The wiring for the l293D chip is not as complicated as it may look. The +V and the +Vmotor pins should be connected to and external power supply. In this project we will end up connecting these to the battery pack from the RC car. The IN1, IN2, IN3 and IN4 pins will be connected to pins 3,4,5 and 6 on the Arduino. The two motor enable pins are connected to pins 9 and 10 on the Arduino. The OUT1 and 2 pins go to one motor and OUT3 and 4 go to the other. It doesn't matter which IN pins go to which pins on the Arduino or which OUT pins go to which leads on the motor as these will be defined in code.

Step 4: Receiving Arduino: the Code.

At the top of this sketch, also include the Virtual Wire library:
#include <VirtualWire.h> //Include required library
We will also define some constants that we will use to control our car:
#define FORWARD '8'
#define BACKWARD '5'
#define STOP '0'
#define LEFT '4'
#define RIGHT '6'
#define STRAIGHT '/'


#define TIMEOUT 500UL
Next comes the variables for the motor pins. The value of these variables will be whatever pin the wire is connected to on the Arduino. If you wired yours up differently make sure you update your code here:
int motor1Pin1 = 5;    // pin 2 on L293D is connected to this pin on the Arduino
int motor1Pin2 = 6;    // pin 7 on L293D                  "
int enablePin1 = 9;    // pin 1 on L293D                  "
int motor2Pin1 = 3;    // pin 10 on L293D                 "int motor2Pin2 = 4;    // pin 15 on L293D                 "
int enablePin2 = 10;    // pin 9 on L293D                 "
There are two variables to control the motor speeds:
int motor1Speed = 255; //max speed 255 
int motor2Speed = 255;
The last variable is for the timeout counter:
unsigned long counter;

The setup function will set up the pins as required. It also set's up Virtual Wire:
void setup() 
{  
    //Set pin modes
    pinMode(motor1Pin1, OUTPUT);
    pinMode(motor1Pin2, OUTPUT);
    pinMode(motor2Pin1, OUTPUT);
    pinMode(motor2Pin2, OUTPUT);
    pinMode(enablePin1, OUTPUT);
    pinMode(enablePin2, OUTPUT);
    
    //Set enablePin to the motors speed
    analogWrite(enablePin1, motor1Speed);
    analogWrite(enablePin2, motor2Speed);
    
    //Begin with motors off
    digitalWrite(motor1Pin1, LOW); 
    digitalWrite(motor1Pin2, LOW);
    digitalWrite(motor2Pin1, LOW); 
    digitalWrite(motor2Pin2, LOW);


    Serial.begin(9600);
    //Initialise the IO and ISR
    vw_set_ptt_inverted(true);    // Required for RX Link Module
    vw_setup(2000);                   // Bits per sec
    vw_set_rx_pin(13);           // We will be receiving on pin 23 (Mega) ie the RX pin from the module connects to this pin. 
    vw_rx_start();                      // Start the receiver 
}

The loop function will contain the following code:
void loop()
{
  //variables for Virtual Wire
  uint8_t buf[VW_MAX_MESSAGE_LEN];
  uint8_t buflen = VW_MAX_MESSAGE_LEN;
  
  //If we got a message from the serial port
  if (Serial.available())
  {
    //Reset the timeout counter
    counter = millis();
    //Drive the requested motor
    drive(Serial.read());
    
    //Otherwise check for a message from the reciever
  } else if (vw_get_message(buf, &buflen)) // check to see if anything has been received
    {
      //Reset timeout counter
      counter = millis();
      int i;
      Serial.println("recieved"); //Debug message
       // Message with a good checksum received.
      
      
      //For each command recieved
      for (i = 0; i < buflen; i++)
      {
          drive(buf[i]); //Drive requested motors
      }
      
      
      //If the counter is greater than the timeout (No signal recieved) then stop the car.
    } else if(millis()-counter >= TIMEOUT) {
      drive(STOP);
      drive(STRAIGHT); 
    }
}
Finally we need to define the Drive() function:
void drive(int dir) {
  //Check which direction is requested (same for below)
  if (dir == FORWARD) {
     Serial.println("Forward");//Used for debugging
     
     //Set motor accordingly (same for below)
     digitalWrite(motor1Pin1, HIGH); 
     digitalWrite(motor1Pin2, LOW);
     
  } else if (dir == BACKWARD) {
    Serial.println("Backward");
     digitalWrite(motor1Pin1, LOW); 
     digitalWrite(motor1Pin2, HIGH); 
  } else if (dir == STOP) {
     Serial.println("Stop");
    digitalWrite(motor1Pin1, LOW); 
    digitalWrite(motor1Pin2, LOW); 
  } else if(dir == STRAIGHT) {
    Serial.println("Straight");
    digitalWrite(motor2Pin1, LOW);
    digitalWrite(motor2Pin2, LOW);
  }else if (dir == LEFT) {
    Serial.println("Left");
    digitalWrite(motor2Pin1, LOW);
    digitalWrite(motor2Pin2, HIGH);
  }else if (dir == RIGHT) {
    Serial.println("Right");
    digitalWrite(motor2Pin1, HIGH);
    digitalWrite(motor2Pin2, LOW);
  }
}

You can download the complete sketch from here: RC_CAr.ino

Step 5: Receiving Arduino: Attaching the Car.

For this project you can use any remote control car that uses DC motors.
First, remove the casing of the car. Mine had a bunch of screws which I undid and the casing came straight off.
Next unscrew the circuit board already in the car, we wont be needing that. Cut all the wires connected as close to the circuit board as possible and remove the board. Now we are ready to wire up our car to our Arduino.
Now connect the wires from the motors to the OUT pins of the L293D chip and the wires from the battery pack to the rails that power the chip. The ground of the Arduino should also connect to the negative terminal of the battery pack. Finally, connect the car antenna to the RF receiver antenna pin.

Step 6: Computer Program: Creating the GUI.

For the computer program, create a new Java application in whatever application you are using. I recommend Eclipse.
First create a class that extends JPanel which will be used as our GUI. It will also need to implement ActionListener and KeyListener.
public class GUI extends JPanel implements ActionListener, KeyListener {

}
In the constructor of our class we will be setting up the GUI layout. In order to achieve the layout above, we will be using a 3 by 4 grid. Note that this does not need to be defined anywhere, we just tell the components what grid space to occupy and the grid is created accordingly. The code below is the constructor for the GUI class and contains the layout of our GUI.

	public JButton bFront, bBack, bLeft, bRight; //Define the buttons
	
	StyledDocument instructionsText; //This is used for the instructions
	public JTextPane instructions;	//Also used for instructions
		
	public GUI() {
		
		setLayout(new GridBagLayout()); //Set the layout to Grid Bag
		this.setPreferredSize(new Dimension(400,400));//Set the size of the window
		GridBagConstraints c = new GridBagConstraints();
		c.fill = GridBagConstraints.BOTH;
		c.gridx = 0; //This will be the position of the instructions
		c.gridy = 0;
		
		c.gridwidth = 4; //The instructions will span 4 tiles
		
                //Create the instructions text
		StyledDocument instructionsText = new DefaultStyledDocument();
		Style defaultStyle = instructionsText.getStyle(StyleContext.DEFAULT_STYLE);
		StyleConstants.setAlignment(defaultStyle, StyleConstants.ALIGN_CENTER);
		
		try {
			instructionsText.insertString(0, "Control the car using the keyboard.\n Change the key bindings by clicking the buttons below.", null);
		} catch (BadLocationException e) {
			e.printStackTrace();
		}
		
                //add the text to the pane
		instructions = new JTextPane(instructionsText);
		instructions.setFocusable(false);
		//add the pane to the window
		add(instructions,c);
		
		//Create the forward button (same for others below)
		bFront = new JButton("Forward key: " + KeyEvent.getKeyText(frontKey));
		bFront.setActionCommand("forward");
		bFront.addActionListener(this);
		bFront.setFocusable(false);


		//Set position of the forward button (same for others below)
		c.gridwidth = 1;
		c.gridx = 1;
		c.gridy = 1;
		add(bFront,c);
		
		
		bBack = new JButton("Backward key: " + KeyEvent.getKeyText(backKey));
		bBack.setActionCommand("backward");
		bBack.addActionListener(this);
		bBack.setFocusable(false);
		
		c.gridx = 1;
		c.gridy = 4;
		add(bBack,c);
		
		bLeft = new JButton("Left key: " + KeyEvent.getKeyText(leftKey));
		bLeft.setActionCommand("left");
		bLeft.addActionListener(this);
		bLeft.setFocusable(false);
		
		c.gridx = 0;
		c.gridy = 3;
		add(bLeft,c);
		 
		
		bRight = new JButton("Right key: "+ KeyEvent.getKeyText(rightKey));
		bRight.setActionCommand("right");
		bRight.addActionListener(this);
	    bRight.setFocusable(false);
	    
	    c.gridx = 3;
		c.gridy = 3;
		add(bRight,c);
		
		setFocusable(true);
		addKeyListener(this);


	}
With our class defined we need to create the main method which will run and show our GUI:
	 public static void main(String[] args) {
		 // TODO Auto-generated method stub
		
		 //Create and show the GUI
		javax.swing.SwingUtilities.invokeLater(new Runnable() {
		 public void run() {
			 createAndShowGUI();			 
	        }
	    });	 
	}
	
	 private static void createAndShowGUI() {
		//Create and set up the window.
		JFrame frame = new JFrame("Arduino RC Car Controller");


		
		frame.setLocationRelativeTo(null);
		//Tell the program to exit when the GUI is closed
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
		
		final GUI gui = new GUI(); //create the GUI
		gui.setOpaque(true); //Set it to visible
		frame.setContentPane(gui); //attach it to the Jframe
				 
		//Display the window.
		frame.pack();
		frame.setVisible(true);
	 }
Next we need to add the functionality for when we click on a button:
@Override
//If a button is clicked tell the program to listen for the next key pressed
public void actionPerformed(ActionEvent e) {

	if(e.getActionCommand().equals("forward")) {
		listenKey = 1;
	} else if(e.getActionCommand().equals("backward")) {
		listenKey = 2;
	} else if(e.getActionCommand().equals("left")) {
		listenKey = 3;
	} else if(e.getActionCommand().equals("right")) {
		listenKey = 4;
	}		
}
We also need to handle keyboard presses:
@Override
public void keyPressed(KeyEvent e) {
	//When a key is pressed
	
	//if not listening for a key change the color of the corresponding button 
	//and save the state of the key
	if(listenKey <= 0)
		if(e.getKeyCode() == frontKey) {
			bFront.setBackground(Color.yellow);
			frontKeyPressed = true;
		} else if(e.getKeyCode() == backKey) {
			bBack.setBackground(Color.yellow);
			backKeyPressed = true;
		} else if(e.getKeyCode() == leftKey) {
			bLeft.setBackground(Color.yellow);
			leftKeyPressed = true;
		} else if(e.getKeyCode() == rightKey) {
			bRight.setBackground(Color.yellow);
			rightKeyPressed = true;
		} 
}

@Override
public void keyReleased(KeyEvent e) {
	//When a key is released
	
	//If we are waiting for a key
	switch(listenKey) {
		
		//Set the control for the requred button
		case 1:
			frontKey = e.getKeyCode();
			bFront.setText("Forward key: " + KeyEvent.getKeyText(frontKey));
			listenKey = 0;
			break;
		
		case 2:
			
			backKey = e.getKeyCode();
			bBack.setText("Backward key: " + KeyEvent.getKeyText(backKey));
			listenKey = 0;
			break;
			
		case 3:
			leftKey = e.getKeyCode();
			bLeft.setText("Left key: " + KeyEvent.getKeyText(leftKey));
			listenKey = 0;
			break;
			
		case 4:
			rightKey = e.getKeyCode();
			bRight.setText("Right key: " + KeyEvent.getKeyText(rightKey));
			listenKey = 0;
			break;
		
			//Otherwise return the button to normal
		default:
			if(e.getKeyCode() == frontKey) {
				bFront.setBackground(null);
				frontKeyPressed = false;
			} else if(e.getKeyCode() == backKey) {
				bBack.setBackground(null);
				backKeyPressed = false;
			} else if(e.getKeyCode() == leftKey) {
				bLeft.setBackground(null);
				leftKeyPressed = false;
			} else if(e.getKeyCode() == rightKey) {
				bRight.setBackground(null);
				rightKeyPressed = false;
			} 
			break;
	
	}
}

Download the class file so far: GUI.java

Step 7: Computer Program: Coding the Serial Connection.

Finally we'll add the serial port functionality to our program.
To do this we need to download the RXTX library from: http://rxtx.qbang.org/wiki/index.php/Download
Place the rxtxSerial.dll and the RXTXcomm.jar into the project folder and add it to the build path in Project>Properties>Java Build Path.
Next we'll create a separate thread which we'll put at the bottom of the createAndShowGUI() function:
Thread t=new Thread() {
	public void run() {

		//Attempt to initialize the serial connection
		while(!gui.initialize()) {
			System.out.print("Waiting one second...");
			try {Thread.sleep(1000);} catch (InterruptedException ie) {}
		}
		
		//Send data every 100ms
		while(true) {
			gui.sendData();
			try {Thread.sleep(100);} catch (InterruptedException ie) {}
		}
	}
};
t.start();
System.out.println("Started");
The sendData() function is as follows:
public void sendData(){
	try {
		//If forward key is pressed send forward etc...
		if(frontKeyPressed){
			output.write(FORWARD);
		} else if(backKeyPressed){
			output.write(BACKWARD);
		} else {
			output.write(STOP);
		}
		
		if(leftKeyPressed){
			//If both left and right are pressed the car will go straight
			if(rightKeyPressed)
				output.write(STRAIGHT); 
			else
				output.write(LEFT);
		} else if (rightKeyPressed){
			output.write(RIGHT);
		} else {
			output.write(STRAIGHT);
		}
		
	} catch(Exception e) {
		e.printStackTrace();
	}
}
Download the complete class file here: https://dl.dropboxusercontent.com/u/63127140/GUI.j..

Step 8: Finalising Everything.

Once you are happy that your car is working you can solder the wiring permanently onto your prototype board. If you do not know how to solder, check out this tutorial: https://learn.sparkfun.com/tutorials/how-to-solde...

Further improvements:
  • If your RC car has any LED lights, try hooking them up to your Arduino too. You could use a photosensitive resistor to detect when it gets dark and turn the lights on accordingly.
  • Another cool addition could be to add a wireless camera to see what the car see's while you drive it around.
  • For advanced programmers, In addition to a wireless camera try creating a web interface to control your car from anywhere in the world.
  • Try adding different sensors to the car and give it some artificial intelligence.

Share your ideas or show off your designs in the comment section below!
Hardware Hacking

Participated in the
Hardware Hacking