Remote Control Car

Overview:

This project makes use of an Arduino, a car chassis, a bluetooth module, and an android phone to make a Remote Control Car that is controlled by tilting the phone like a steering wheel. This post is a high level overview of the project and requires a solid understanding of coding.

Relevance to Design:

This project was created to test the communication capabilities between the android phone and custom hardware devices (in this case a car). In particular this project is interested in getting sensor information from the phone (the accelerometer) and translating it into an actualised output (The steering of a car). As android phones are ubiquitous and are capable of sensing a range of information, it is a useful skill for a design student to see how this information might be collected and used in devices they might design.

Design Process:

A project like this has many steps, I used a process of computational thinking to break down the project into bite sized chunks. An example of such chunks is as follows:

 

  • Test reading accelerometer information from the android device
  • Establish communication between Arduino and processing
  • Send accelerometer information to Arduino
  • Translate accelerometer information to a output to drive the motor

 

 Technical overview:

This project is split into three main parts:

  1. Wiring the circuit diagram and building car
  2. Creating the android app in Processing to send the tilt information
  3. writing the code for the Arduino microcontroller in the car to receive the tilt information and translate into a motor output

 

Wiring the circuit diagram and building car

You can download the chassis to be laser cut or printed here: /https://www.thingiverse.com/thing:2586084

for the wiring please look at the following diagram made in fritzing Wiring Diagram from https://www.youtube.com/watch?v=zGjD1TbTZmE

Creating the android app in Processing:

I used the ketai library for android processing. I combined the example scripts for the accelerometer and  Bluetooth connection to create my own processing script:



// Sensor decliartions
KetaiSensor sensor;
float accelerometerX, accelerometerY, accelerometerZ;

PFont fontMy;
boolean bReleased = true; //no permament sending when finger is tap
KetaiBluetooth bt;
boolean isConfiguring = true;
String info = "";
KetaiList klist;
ArrayList devicesDiscovered = new ArrayList();

//********************************************************************
// The following code is required to enable bluetooth at startup.
//********************************************************************

void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bt = new KetaiBluetooth(this);
getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}

void onActivityResult(int requestCode, int resultCode, Intent data) {
bt.onActivityResult(requestCode, resultCode, data);

}

void setup() {
size(displayWidth, displayHeight);
sensor = new KetaiSensor(this);
sensor.start();
orientation(LANDSCAPE);
textAlign(CENTER, CENTER);
textSize(36);


//start listening for BT connections
bt.start();
//at app start select device…
isConfiguring = true;
//font size
fontMy = createFont("SansSerif", 40);
textFont(fontMy);
}

void draw() {
//at app start select device
if (isConfiguring)
{
ArrayList names;
background(78, 93, 75);
klist = new KetaiList(this, bt.getPairedDeviceNames());
isConfiguring = false;
}
else
{


background(78, 93, 75);
text("Accelerometer: \n" + 
"x: " + nfp(accelerometerX, 1, 3) + "\n" +
"y: " + nfp(accelerometerY, 1, 3) + "\n" +
"z: " + nfp(accelerometerZ, 1, 3), 0, 0, width, height);


String s = "<" + int(((accelerometerX+10)*25.5)) + "," + int(((accelerometerY+10)*25.5)) + ">";
char[] a = s.toCharArray();
byte[] b = byte(a);
bt.broadcast(b);

}
}

void onKetaiListSelection(KetaiList klist) {
String selection = klist.getSelection();
bt.connectToDeviceByName(selection);
//dispose of list for now
klist = null;
}

//Call back method to manage data received
void onBluetoothDataEvent(String who, byte[] data) {
if (isConfiguring)
return;
//received
info += new String(data);
//clean if string to long
if(info.length() > 150)
info = "";
}

void onAccelerometerEvent(float x, float y, float z)
{
accelerometerX = x;
accelerometerY = y;
accelerometerZ = z;
}
// Arduino+Bluetooth+Processing 
// Arduino-Android Bluetooth communication

code for the arduino microcontroller

Reliably reading in the data coming from the android phone was the most difficult part of this project. This blog post taught me everything I needed to know and reading serial data and it what my code is based on. My Arduino code is as follows:

#include <SoftwareSerial.h>

SoftwareSerial hc06(2,3);
String data = "";



// Example 5 - Receive with start- and end-markers combined with parsing

const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars];        // temporary array for use when parsing

      // variables to hold the parsed data
char messageFromPC[numChars] = {0};
int x = 0;
int y = 0;
int z = 0;
int motorSpeed;
int left;
int right;
int dir;

int IN1 = 10; 
int IN2 = 11; 
int IN3 = 9;
int IN4 = 6;

boolean newData = false;

//============

void setup() {
    Serial.begin(9600);

      //Initialize Bluetooth Serial Port
  hc06.begin(9600);

  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT);
  pinMode(IN4, OUTPUT);
}

//============

void loop() {
showParsedData();
    recvWithStartEndMarkers();
    if (newData == true) {
        strcpy(tempChars, receivedChars);
            // this temporary copy is necessary to protect the original data
            //   because strtok() used in parseData() replaces the commas with \0
        parseData();
        ControlCar();
        newData = false;
    }
    
}

//============

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = '<';
    char endMarker = '>';
    char rc;

    while (hc06.available() > 0 && newData == false) {
        rc = hc06.read();

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }

}

//============

void parseData() {      // split the data into its parts

    char * strtokIndx; // this is used by strtok() as an index

    strtokIndx = strtok(tempChars,",");      // get the first part - the string
    x = atoi(strtokIndx);     // convert this part to an integer
 
    strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
    y = atoi(strtokIndx);     // convert this part to an integer



}

//============

void showParsedData() {
//    Serial.print("Z ");
//    Serial.println(z);
    Serial.print("X ");
    Serial.println(x);
    Serial.print("Y ");
    Serial.println(y);
}

void ControlCar(){

 dir = map(y,-100,650,-255,255);
//  Serial.print("motorSpeed");
// Serial.println(motorSpeed);
//Serial.print("dir");
//Serial.println(dir);
 
  if (x < 258){
    //move foward
    motorSpeed = map(x,257,0,1,255); // 1-255 foward
    rightFoward();
    leftFoward();
    }else{
     //move back
    motorSpeed = map(x,258,550,1,255); // 1-255 backwards
    
    rightBack();
    leftBack();


   
}

  }

void leftFoward()
{
  analogWrite(IN1, constrain(motorSpeed - dir,1,255));
  digitalWrite(IN2, LOW);
//  Serial.print("left Foward");
//  Serial.println(constrain(motorSpeed - dir,1,255));

}

void rightFoward()
{
  analogWrite(IN3, constrain(motorSpeed + dir,1,255));
  digitalWrite(IN4, LOW);
//  Serial.print("right Foward");
//  Serial.println(constrain(motorSpeed + dir,1,255));  
}

void leftBack()
{
  analogWrite(IN2, constrain(motorSpeed - dir,1,255));
  digitalWrite(IN1, LOW);
 // Serial.print("left back");
 // Serial.println(constrain(motorSpeed - dir,1,255));
}

void rightBack()
{
  analogWrite(IN4, constrain(motorSpeed + dir,1,255));
  digitalWrite(IN3, LOW);
//  Serial.print("right back");
//  Serial.println(constrain(motorSpeed + dir,1,255));  
}

void stopMotors()
{
 digitalWrite(IN1, LOW);
 digitalWrite(IN2, LOW);
 digitalWrite(IN3, LOW);
 digitalWrite(IN4, LOW);
}