October 2, 2025
Gesture-Controlled Bluetooth Robot Car Using Arduino & MPU6050

Building a Gesture -Bluetooth-Controlled Robot Car Using Arduino & MPU6050: Complete Guide

Today, we are going to build an exciting robotics project where an Arduino-based robot car is controlled using the MPU6050 sensor (Accelerometer & Gyroscope) and Bluetooth communication. The MPU6050 sensor will detect hand movements and translate them into motion commands, which will be sent via an HC-05 Bluetooth module to control the robot car’s movement in real time.

Additionally, to make the robot more autonomous, we will integrate an HC-SR04 Ultrasonic Sensor. This sensor will continuously monitor the surroundings and detect obstacles in the path of the robot. If an obstacle is detected, the robot will automatically stop or change direction to avoid collisions, making it a smart and efficient system.

This project is perfect for students, hobbyists, and tech enthusiasts who are eager to explore robotics, microcontrollers, and IoT. It provides hands-on experience in working with motion sensors, wireless communication, and obstacle detection, making it a great learning opportunity for anyone interested in building intelligent robotic systems.


Required Components

To build this robot car, you will need the following components:

No.ComponentQuantity
1Arduino Uno1
2MPU6050 (Accelerometer & Gyroscope Sensor)1
3HC-05 Bluetooth Module1
4L298N Motor Driver Module1
5DC Motor (Wheel Motor)4
6HC-SR04 Ultrasonic Sensor1
79V Battery / Li-ion Battery Pack1
8Jumper Wires & Battery ConnectorsAs needed
9Robot Car Chassis1

Circuit Connections

1. Connecting MPU6050 (Accelerometer & Gyroscope):

  • VCC → 5V (Arduino)
  • GND → GND (Arduino)
  • SCL → A5 (Arduino)
  • SDA → A4 (Arduino)

2. Connecting HC-05 Bluetooth Module:

  • VCC → 5V (Arduino)
  • GND → GND (Arduino)
  • TX → 11 (Arduino)
  • RX → 10 (Arduino) [Use a 1KΩ & 2KΩ voltage divider]

3. Connecting L298N Motor Driver:

  • IN1 → 3 (Arduino)
  • IN2 → 5 (Arduino)
  • IN3 → 6 (Arduino)
  • IN4 → 9 (Arduino)
  • Motor A (Left) → M1 & M2
  • Motor B (Right) → M3 & M4
  • 12V → Battery positive terminal
  • GND → Battery negative terminal

4. Connecting HC-SR04 Ultrasonic Sensor:

  • VCC → 5V (Arduino)
  • GND → GND (Arduino)
  • Trig → 12 (Arduino)
  • Echo → 13 (Arduino)
Circuit Connections

Arduino Code Explanation

1. Reading Data from Gesture MPU6050

#include <Wire.h>

const int MPU=0x68;  
float AccX, AccY, AccZ;
float pitch, roll;

void setup(){
  Wire.begin();
  Wire.beginTransmission(MPU);
  Wire.write(0x6B);
  Wire.write(0);
  Wire.endTransmission(true);
  Serial.begin(9600);
}

void loop(){
  Wire.beginTransmission(MPU);
  Wire.write(0x3B);
  Wire.endTransmission(false);
  Wire.requestFrom(MPU, 6, true);
  
  AccX = (Wire.read() << 8 | Wire.read()) / 16384.0;
  AccY = (Wire.read() << 8 | Wire.read()) / 16384.0;
  AccZ = (Wire.read() << 8 | Wire.read()) / 16384.0;

  pitch = atan(AccY / sqrt(AccX * AccX + AccZ * AccZ)) * 180 / PI;
  roll = atan(-AccX / sqrt(AccY * AccY + AccZ * AccZ)) * 180 / PI;

  Serial.print("Pitch: "); Serial.print(pitch);
  Serial.print(" Roll: "); Serial.println(roll);
  delay(100);
}

This code reads the pitch and roll values from the MPU6050 sensor to detect hand gestures.

The code for the transmission unit has been provided in the latter part of this project

2. Receiving Bluetooth Commands and Controlling the Robot

#include <SoftwareSerial.h>

SoftwareSerial BTSerial(10, 11);  // HC-05 connection

void setup() {
  BTSerial.begin(9600);
  pinMode(3, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(9, OUTPUT);
}

void loop() {
  if (BTSerial.available()) {
    char command = BTSerial.read();
    if (command == 'F') {  // Move forward
      digitalWrite(3, HIGH);
      digitalWrite(5, LOW);
      digitalWrite(6, HIGH);
      digitalWrite(9, LOW);
    } else if (command == 'B') {  // Move backward
      digitalWrite(3, LOW);
      digitalWrite(5, HIGH);
      digitalWrite(6, LOW);
      digitalWrite(9, HIGH);
    } else if (command == 'L') {  // Turn left
      digitalWrite(3, LOW);
      digitalWrite(5, HIGH);
      digitalWrite(6, HIGH);
      digitalWrite(9, LOW);
    } else if (command == 'R') {  // Turn right
      digitalWrite(3, HIGH);
      digitalWrite(5, LOW);
      digitalWrite(6, LOW);
      digitalWrite(9, HIGH);
    } else {  // Stop
      digitalWrite(3, LOW);
      digitalWrite(5, LOW);
      digitalWrite(6, LOW);
      digitalWrite(9, LOW);
    }
  }
}

This code processes Bluetooth commands and moves the robot accordingly.


3. Obstacle Detection and Avoidance

This code uses an ultrasonic sensor to detect obstacles and stop the robot if needed.

Receiver cod

#include <SoftwareSerial.h>  
  
SoftwareSerial BTSerial(10, 11); //  CONNECT BT RX PIN TO ARDUINO 12 PIN | CONNECT BT TX PIN TO ARDUINO 11 PIN  
#include<SPI.h>  
  
    
int trigPin = 12;    // Trig  
int echoPin = 13;    // Echo  
long duration,  cm, inches;  
int direct;  
int l1=3;  
int r1=5;  
int l2=6;  
int r2=9;  
  
int  forword_corr;  
  
  
int a = 0 ;  
int s = 3;   
  
  
int b = 0 ;  
int  c = 2;   
  
  
  
  
  
void setup() {  
  Serial.begin(9600);        
  BTSerial.begin(9600);      
    
   
  pinMode(l1, OUTPUT);  
  pinMode(r1,  OUTPUT);  
  pinMode(l2, OUTPUT);  
  pinMode(r2, OUTPUT);  
    
  digitalWrite(l1,  LOW);  
  digitalWrite(r1, LOW);  
  digitalWrite(l2, LOW);  
  digitalWrite(r2,  LOW);  
    
  pinMode(trigPin, OUTPUT);  
  pinMode(echoPin, INPUT);  
}  
  
void  loop() {  
    
   digitalWrite(trigPin, LOW);  
  delayMicroseconds(5);  
  digitalWrite(trigPin, HIGH);  
  delayMicroseconds(10);  
  digitalWrite(trigPin,  LOW);  
  
  pinMode(echoPin, INPUT);  
  duration = pulseIn(echoPin, HIGH);  
  
  cm = (duration/2) / 29.1;     // Divide by 29.1 or multiply by 0.0343  
  Serial.print(cm);  
  Serial.print("cm");  
  Serial.println();  
  if(BTSerial.available()>0){  // Witing for data incoming from the other XBee module  
   direct=BTSerial.read();  
      
                if(direct == 'B'){  
            Serial.println("Backward");  
            backward();  
          }else if (direct=='F'){  
              Serial.println("Forward");  
            forward();  
          }else if(direct == 'R'){  
            Serial.println("Right");  
            left();  
          }else if(direct == 'L'){  
            Serial.println("left");  
            right();  
  
           }else if(direct == 'r'){  
            Serial.println("rotright");  
            rotr();  
              
          }else if(direct == 'l'){  
            Serial.println("rotleft");  
            rotl();  
            }else if  (direct == 'S'){  
            Serial.println("Stop");  
            stopCar();  
          }  
                  
       
             
       
  }  
}  
      
    
  
  
   
void forward()  
{  
  if(cm<=15){  
     digitalWrite(l1, LOW);  
      digitalWrite(r2, LOW);  
  
    }  
    else{  
    digitalWrite(l1, HIGH);  
  digitalWrite(r1, LOW);  
  digitalWrite(l2, LOW);  
    
    
    digitalWrite(r2, LOW);  
 delay(2);  
    digitalWrite(r2, HIGH);  
   delay(2);  
}  
}  
void backward()  
{  
  digitalWrite(l1, LOW);  
  digitalWrite(l2,  HIGH);  
  digitalWrite(r2, LOW);  
  if(b%c == 0){  
    digitalWrite(r1, HIGH);  
  }else{  
    digitalWrite(r1, LOW);  
   }  
     
  
    if(b > 1000 ) {  
     b=0;  
   }  
}  
void right()  
{  
  digitalWrite(l1, HIGH);  
  digitalWrite(r1,  LOW);  
  digitalWrite(l2, LOW);  
  analogWrite(r2, 100);  
   
      
  
}  
void  left()  
{  
   analogWrite(l1, 100);  
  digitalWrite(r1, LOW);  
  digitalWrite(l2,  LOW);  
  digitalWrite(r2, HIGH);  
}  
  
void stopCar() {  
    digitalWrite(l1,  LOW);  
  
  digitalWrite(r1, LOW);  
  digitalWrite(l2, LOW);  
  digitalWrite(r2,  LOW);  
}  
void rotr() {  
  
  digitalWrite(l1, HIGH);  
  digitalWrite(r1,  LOW);  
  digitalWrite(l2, HIGH);  
  digitalWrite(r2, LOW);  
}  
void rotl(){  
  
  digitalWrite(l1, LOW);  
  digitalWrite(r1, HIGH);  
  digitalWrite(l2, LOW);  
  digitalWrite(r2, HIGH);  
}  

#include  <Wire.h>  
#include <SoftwareSerial.h>  
SoftwareSerial BTSerial(10, 11); // CONNECT  BT RX PIN TO ARDUINO 11 PIN | CONNECT BT TX PIN TO ARDUINO 10 PIN  
  
  
const  int MPU_ADDRESS = 0x68; // MPU6050 I2C address  
  
float AccX, AccY, AccZ;  
float  GyroX, GyroY, GyroZ;  
  
float accAngleX, accAngleY, gyroAngleX, gyroAngleY,  gyroAngleZ;  
float roll, pitch, yaw;  
  
float AccErrorX, AccErrorY, GyroErrorX,  GyroErrorY, GyroErrorZ;  
  
float elapsedTime, currentTime, previousTime;  
  
int  c = 0;  
  
  
void setup()  
{  
    
  Serial.begin(9600);  
  BTSerial.begin(9600);  // HC-05 default speed in AT command more  
  
  Wire.begin();                      //  Initialize comunication  
  Wire.beginTransmission(MPU_ADDRESS);       // Start  communication with MPU6050 // MPU=0x68  
  Wire.write(0x6B);                  //  Talk to the register 6B  
  Wire.write(0x00);                  // Make reset -  place a 0 into the 6B register  
  Wire.endTransmission(true);        //end the  transmission  
    
    
  // Configure Accelerometer Sensitivity - Full Scale  Range (default +/- 2g)  
/*  Wire.beginTransmission(MPU);  
  Wire.write(0x1C);                  //Talk to the ACCEL_CONFIG register (1C hex)  
  Wire.write(0x10);                  //Set the register bits as 00010000 (+/- 8g full scale range)  
  Wire.endTransmission(true);  
    
  // Configure Gyro Sensitivity - Full Scale  Range (default +/- 250deg/s)  
  Wire.beginTransmission(MPU);  
  Wire.write(0x1B);                   // Talk to the GYRO_CONFIG register (1B hex)  
  Wire.write(0x10);                   // Set the register bits as 00010000 (1000deg/s full scale)  
  Wire.endTransmission(true);  
  delay(20);  
    
    
  //Call this function  if you need to get the IMU error values for your module*/  
 //while(true) calculate_IMU_error();  
    
 // delay(20);  
    
}  
  
void loop()  
{  
    
  MPU_read_accel_data();  
  MPU_read_gyro_data();  
  
   
  // calculate time elapsed since last time  we were here   
  previousTime = currentTime;        // Previous time is stored  before the actual time read  
  currentTime = millis();            // Current time  actual time read  
  elapsedTime = (currentTime - previousTime) / 1000; // Divide  by 1000 to get seconds  
  
  
  // Complementary filter - combine acceleromter  and gyro angle values  
  roll  = 0.92 * (roll  + (GyroX * elapsedTime)) + 0.08  * accAngleX;  
  pitch = 0.92 * (pitch + (GyroY * elapsedTime)) + 0.08 * accAngleY;  
  //yaw   = gyroAngleZ;  
  
  
  // Currently the raw values are in degrees  per seconds, deg/s, so we need to multiply by sendonds (s) to get the angle in degrees  
  gyroAngleX += GyroX * elapsedTime; // deg/s * s = deg  
  gyroAngleY += GyroY  * elapsedTime;  
  gyroAngleZ += GyroZ * elapsedTime;  
    
  /*by me */  
  // Print the values on the serial monitor  
    Serial.print("roll:");  
    Serial.print(roll);  
    Serial.print("    ");  
  
    Serial.print("pitch:");  
    Serial.println(pitch);  
      
   /* Serial.print("    ");  
    Serial.print("yaw:");  
    Serial.println(yaw);*/  
    
    
  
  
  /*Serial.print(" ");  
/*  made by me  
  // Print the values on the serial monitor  
  Serial.print(pitch);  
  Serial.print(" ");  
  Serial.print(accAngleY);  
  Serial.print(" ");  
  Serial.println(gyroAngleY);  
*/  
if(pitch<-17){  
      BTSerial.write('F');  
    }else if(pitch>20){  
      BTSerial.write('B');  
    }else if(roll>30){  
      BTSerial.write('R');  
    }else if(roll<-30){  
      BTSerial.write('L');  
    }else if(yaw>30){  
      BTSerial.write('r');  
    }else if(yaw<-30){  
      BTSerial.write('l');  
    }else   
      BTSerial.write('S');  
}  
  
void  MPU_read_accel_data()  
{  
  // === Read acceleromter data === //  
  Wire.beginTransmission(MPU_ADDRESS);  
  Wire.write(0x3B); // Start with register 0x3B (ACCEL_XOUT_H)  
  Wire.endTransmission(false);  
  Wire.requestFrom(MPU_ADDRESS, 6, true); // Read 6 registers total, each axis value  is stored in 2 registers  
    
  //For a range of +-2g, we need to divide the  raw values by 16384, according to the datasheet  
  AccX = (Wire.read() << 8 |  Wire.read()) / 16384.0; // X-axis value  
  AccY = (Wire.read() << 8 | Wire.read())  / 16384.0; // Y-axis value  
  AccZ = (Wire.read() << 8 | Wire.read()) / 16384.0;  // Z-axis value  
    
  // Calculating Roll and Pitch from the accelerometer  data  
  accAngleX = (atan(AccY / sqrt(pow(AccX, 2) + pow(AccZ, 2))) * 180 / PI)      - (-0.40);  // AccErrorX ~(0.58) See the calculate_IMU_error()custom function  for more details  
  accAngleY = (atan(-1 * AccX / sqrt(pow(AccY, 2) + pow(AccZ,  2))) * 180 / PI) - (-3.75); // AccErrorY ~(-1.58)  
    
}  
  
  
void MPU_read_gyro_data()  
{  
  // === Read gyroscope data === //  
  Wire.beginTransmission(MPU_ADDRESS);  
  Wire.write(0x43); // Gyro data first register address 0x43  
  Wire.endTransmission(false);  
  Wire.requestFrom(MPU_ADDRESS, 6, true); // Read 4 registers total, each axis value  is stored in 2 registers  
  
  // For a 250deg/s range we have to divide first  the raw value by 131.0, according to the datasheet  
  GyroX = (Wire.read() <<  8 | Wire.read()) / 131.0;  
  GyroY = (Wire.read() << 8 | Wire.read()) / 131.0;  
  GyroZ = (Wire.read() << 8 | Wire.read()) / 131.0;  
    
  // Correct the outputs  with the calculated error values  
  GyroX = GyroX - (-0.68); // GyroErrorX = ~  (-2.12)   
  GyroY = GyroY - (-2.48);  // GyroErrorY = ~ (4.12)  
  GyroZ = GyroZ  - (-0.12);  // GyroErrorZ = ~ (1.20)  
  
  
}  
  
  
void calculate_IMU_error()  
{  
    
  // We can call this funtion in the setup section to calculate the accelerometer  and gyro data error. From here we will get the error values used in the above equations  printed on the Serial Monitor.  
  // Note that we should place the IMU flat in  order to get the proper values, so that we then can the correct values  
  // Read  accelerometer values 200 times  
  while (c < 200)  
  {  
    Wire.beginTransmission(MPU_ADDRESS);  
    Wire.write(0x3B);  
    Wire.endTransmission(false);  
    Wire.requestFrom(MPU_ADDRESS,  6, true);  
    AccX = (Wire.read() << 8 | Wire.read()) / 16384.0 ;  
    AccY  = (Wire.read() << 8 | Wire.read()) / 16384.0 ;  
    AccZ = (Wire.read() << 8 |  Wire.read()) / 16384.0 ;  
    // Sum all readings  
    AccErrorX = AccErrorX  + ((atan((AccY) / sqrt(pow((AccX), 2) + pow((AccZ), 2))) * 180 / PI));  
    AccErrorY  = AccErrorY + ((atan(-1 * (AccX) / sqrt(pow((AccY), 2) + pow((AccZ), 2))) * 180  / PI));  
    c++;  
  }  
    
  //Divide the sum by 200 to get the error value  
  AccErrorX = AccErrorX / 200;  
  AccErrorY = AccErrorY / 200;  
    
  c =  0;  
    
  // Read gyro values 200 times  
  while (c < 200)  
  {  
    Wire.beginTransmission(MPU_ADDRESS);  
    Wire.write(0x43);  
    Wire.endTransmission(false);  
    Wire.requestFrom(MPU_ADDRESS,  6, true);  
      
    GyroX = Wire.read() << 8 | Wire.read();  
    GyroY =  Wire.read() << 8 | Wire.read();  
    GyroZ = Wire.read() << 8 | Wire.read();  
      
    // Sum all readings  
    GyroErrorX = GyroErrorX + (GyroX / 131.0);  
    GyroErrorY = GyroErrorY + (GyroY / 131.0);  
    GyroErrorZ = GyroErrorZ +  (GyroZ / 131.0);  
      
    c++;  
      
  }  
    
  //Divide the sum  by 200 to get the error value  
  GyroErrorX = GyroErrorX / 200;  
  GyroErrorY  = GyroErrorY / 200;  
  GyroErrorZ = GyroErrorZ / 200;  
    
  // Print the  error values on the Serial Monitor  
    
  Serial.print("AccErrorX: ");  
  Serial.print(AccErrorX);  
  Serial.print(" | AccErrorY: ");  
  Serial.print(AccErrorY);  
  Serial.print(" | GyroErrorX: ");  
  Serial.print(GyroErrorX);  
  Serial.print("  | GyroErrorY: ");  
  Serial.print(GyroErrorY);  
  Serial.print(" | GyroErrorZ:  ");  
  Serial.println(GyroErrorZ);  
    
  
  delay(1000);  }

About The Author

1 thought on “Gesture Bluetooth-Controlled Robot Car Using Arduino

  1. I’ve been browsing online greater than 3 hours lately, but I never found any interesting article like yours. It is pretty value sufficient for me. In my opinion, if all webmasters and bloggers made just right content as you probably did, the web might be a lot more helpful than ever before.

Leave a Reply

Your email address will not be published. Required fields are marked *