Gesture controlled car build from scratch
This is a Bluetooth-controlled car with servo motor steering, operated by a gesture-based remote. The entire car is designed from scratch and is 3D printable.
Components and supplies
2
Battery 7.4V lithium
4
DC MOTOR WHEELS
2
Geared DC Motor
1
L289N Motor Driver
1
ESP 32
40
Jumper Wires
1
mpu-6050 gyro and accelerometer
1
Servo motor
1
ARDUINO UNO R3
1
HC-05 Bluetooth Module
Tools and machines
1
Friend with a 3D printer
1
Soldering kit
Apps and platforms
1
Arduino IDE
Project description
Code
Arduino Car
cpp
This is the code for the Car on the Arduino
1#include <Arduino.h> 2#include <Servo.h> 3 4// Motor A control pins 5#define ENA 5 // PWM speed control for motor A 6#define IN1 8 // Direction control pin 1 for motor A 7#define IN2 9 // Direction control pin 2 for motor A 8 9// Motor B control pins 10#define ENB 6 // PWM speed control for motor B 11#define IN3 10 // Direction control pin 1 for motor B 12#define IN4 11 // Direction control pin 2 for motor B 13 14// LED pin (optional visual indicator) 15#define LED 12 16 17int MAX_SPEED = 255; // Maximum motor speed (PWM value) 18 19Servo servus; // Servo motor for steering 20int lastSteeringAngle = 90; // Last angle sent to the servo 21int tolerance = 2; // Minimum difference required to update steering 22float pitch; // Variable to store pitch value from remote 23 24#define BT Serial // Alias for Serial communication over Bluetooth (HC-05) 25 26void setup() { 27 servus.attach(3); // Attach servo to digital pin 3 28 servus.write(90); // Initialize steering to center position 29 30 pinMode(LED, OUTPUT); // Set LED pin as output 31 digitalWrite(LED, LOW); // Turn LED off initially 32 33 // Set motor pins as output 34 pinMode(ENA, OUTPUT); 35 pinMode(IN1, OUTPUT); 36 pinMode(IN2, OUTPUT); 37 38 pinMode(ENB, OUTPUT); 39 pinMode(IN3, OUTPUT); 40 pinMode(IN4, OUTPUT); 41 42 BT.begin(115200); // Begin Bluetooth communication at 115200 baud 43 44 BT.println("Car Ready"); // Print startup message via Bluetooth 45} 46 47// Sets motor speed and direction based on signed speed value 48void setMotors(int speed) { 49 speed = constrain(speed, -MAX_SPEED, MAX_SPEED); // Limit speed to valid range 50 51 if (speed > 0) { 52 // Move forward 53 digitalWrite(IN1, LOW); 54 digitalWrite(IN2, HIGH); 55 digitalWrite(IN3, HIGH); 56 digitalWrite(IN4, LOW); 57 } else if (speed < 0) { 58 // Move backward 59 digitalWrite(IN1, HIGH); 60 digitalWrite(IN2, LOW); 61 digitalWrite(IN3, LOW); 62 digitalWrite(IN4, HIGH); 63 } else { 64 // Stop 65 digitalWrite(IN1, LOW); 66 digitalWrite(IN2, LOW); 67 digitalWrite(IN3, LOW); 68 digitalWrite(IN4, LOW); 69 } 70 71 // Set PWM speed 72 analogWrite(ENA, abs(speed)); 73 analogWrite(ENB, abs(speed)); 74} 75 76void loop() { 77 // Check if Bluetooth data is available 78 while (BT.available()) { 79 // Read one full message line (ending with \n) 80 String line = BT.readStringUntil('\n'); 81 82 // If button press detected (e.g., "BUTTON:1") 83 if (line.indexOf("BUTTON:1") >= 0) { 84 digitalWrite(LED, HIGH); // Turn LED on 85 } else { 86 digitalWrite(LED, LOW); // Turn LED off 87 } 88 89 // Extract pitch value from message 90 int pitchIndex = line.indexOf("PITCH:"); 91 if (pitchIndex >= 0) { 92 int commaIndex1 = line.indexOf(',', pitchIndex); 93 if (commaIndex1 == -1) commaIndex1 = line.length(); 94 String pitchString = line.substring(pitchIndex + 6, commaIndex1); 95 pitch = pitchString.toFloat(); // Convert pitch string to float 96 97 // Map pitch angle to steering angle (servo range) 98 int steeringAngle = map(pitch, -75, 75, 60, 120); 99 steeringAngle = constrain(steeringAngle, 0, 180); 100 101 // Update servo only if the angle changed significantly 102 if (abs(steeringAngle - lastSteeringAngle) > tolerance) { 103 servus.write(steeringAngle); 104 lastSteeringAngle = steeringAngle; 105 } 106 } 107 108 // Extract roll value from message 109 int rollIndex = line.indexOf("ROLL:"); 110 if (rollIndex >= 0) { 111 int commaIndex2 = line.indexOf(',', rollIndex); 112 String rollString = line.substring(rollIndex + 5, commaIndex2); 113 float roll = rollString.toFloat(); // Convert roll string to float 114 115 // Map roll angle to motor speed 116 int speed = map(roll, -75, 75, -MAX_SPEED, MAX_SPEED); 117 speed = constrain(speed, -MAX_SPEED, MAX_SPEED); 118 119 setMotors(speed); // Set motors with calculated speed 120 } 121 122 delay(10); // Small delay to avoid overloading the loop 123 } 124}
ESP-32 Remote
cpp
This is the code for the Remote on the ESP-32
1#include <Arduino.h> 2#include <Wire.h> 3#include "BluetoothSerial.h" 4#include "esp_bt_device.h" 5 6#define MPU 0x68 // I2C address of the MPU6050 sensor 7BluetoothSerial BT; // Create Bluetooth serial object 8 9float initialRoll = 0; // Initial roll reference (for calibration) 10float initialPitch = 0; // Initial pitch reference (for calibration) 11int16_t accX, accY, accZ; // Raw accelerometer values 12 13// Function to read raw accelerometer data from MPU6050 14void readAccelerometer() { 15 Wire.beginTransmission(MPU); 16 Wire.write(0x3B); // Starting register for accelerometer data 17 Wire.endTransmission(false); 18 Wire.requestFrom(MPU, 6, true); // Request 6 bytes: X, Y, Z (2 bytes each) 19 20 accX = Wire.read() << 8 | Wire.read(); // Combine high and low bytes for X 21 accY = Wire.read() << 8 | Wire.read(); // Combine high and low bytes for Y 22 accZ = Wire.read() << 8 | Wire.read(); // Combine high and low bytes for Z 23} 24 25// Calculates roll angle (tilt sideways) using accelerometer values 26float calcRoll(int16_t ay, int16_t az) { 27 float f_ay = ay / 16384.0; // Convert to 'g' units 28 float f_az = az / 16384.0; 29 return atan2(f_ay, f_az) * 180.0 / PI; // Convert radians to degrees 30} 31 32// Calculates pitch angle (tilt forward/backward) using accelerometer values 33float calcPitch(int16_t ax, int16_t ay, int16_t az) { 34 float f_ax = ax / 16384.0; 35 float f_ay = ay / 16384.0; 36 float f_az = az / 16384.0; 37 return atan2(-f_ax, sqrt(f_ay * f_ay + f_az * f_az)) * 180.0 / PI; 38} 39 40void setup() { 41 Serial.begin(115200); // Start serial monitor for debugging 42 43 Wire.begin(21, 22); // Initialize I2C with custom SDA (21), SCL (22) 44 Wire.beginTransmission(MPU); 45 Wire.write(0x6B); // Power management register 46 Wire.write(0); // Wake up MPU6050 47 Wire.endTransmission(); 48 49 pinMode(16, INPUT); // Input pin for recalibration button 50 51 readAccelerometer(); // First sensor reading 52 initialRoll = calcRoll(accY, accZ); // Save initial roll for reference 53 initialPitch = calcPitch(accX, accY, accZ); // Save initial pitch for reference 54 55 BT.setPin("password"); // Optional: set Bluetooth pairing PIN 56 BT.begin("Name_of_Device"); // Start Bluetooth with custom device name 57 58 Serial.println("Bluetooth started"); 59} 60 61void loop() { 62 readAccelerometer(); // Read accelerometer data 63 64 float roll = calcRoll(accY, accZ) - initialRoll; // Calculate roll offset from reference 65 float pitch = calcPitch(accX, accY, accZ) - initialPitch; // Calculate pitch offset from reference 66 67 int buttonState = digitalRead(16); // Check if recalibration button is pressed 68 69 if (buttonState == HIGH) { 70 // Recalibrate reference angles 71 initialRoll = calcRoll(accY, accZ); 72 initialPitch = calcPitch(accX, accY, accZ); 73 Serial.println("------Calibration successful------"); 74 } 75 76 // Create formatted message to send via Bluetooth 77 // Format: ROLL:<value>,PITCH:<value>,BUTTON:<state> 78 String message = "ROLL:" + String(roll, 2) + ",PITCH:" + String(pitch, 2) + ",BUTTON:" + String(buttonState); 79 80 BT.println(message); // Send message via Bluetooth 81 Serial.println(message); // Print message to serial monitor 82 83 delay(10); // Short delay between messages 84}
Downloadable files
Car
3D printable car
Car.zip
Documentation
Firtzing circuit diagram
Firtzing circuit diagram
Circuit.zip
All Datasheets about the components
Here you can find all datasheets
Datasheets.zip
Comments
Only logged in users can leave comments