Final Project

Obstacle Avoiding
Gesture Controlled Car

A dual-system robotics project integrating ultrasound sensing for obstacle avoidance and IMU-based hand gesture control via ESP-NOW.

How It Works

When the ultrasonic sensors detect a distance less than the allotted threshold, it triggers the obstacle avoidance software. The system then calculates a heuristic-based path to maneuver the car safely around the object.

ESP32 HC-SR04 MPU6050 ESP-NOW

The Motivation

With 1.25 million road-related deaths annually worldwide, safety systems like Automatic Emergency Braking are critical. However, many older or budget vehicles lack these features.

I wanted to prove that effective safety tech doesn't need to be expensive. By utilizing HC-SR04 sensors (~75ยข each), this entire obstacle avoidance system was built for under $50. If a student can build this, the automotive industry can implement it globally.

Circuit Design

Sender Board Circuit

Sender Board (Remote)

ESP32 + MPU6050 Accelerometer + OLED

Receiver Board Circuit

Receiver Board (Car)

ESP32 + 4x HC-SR04 Sensors + Motor Drivers

Source Code

Sender Board Logic

// Obstacle avoidance system is taken out 
#include <esp_now.h>
#include <WiFi.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_SSD1306.h>
#include <Wire.h>

Adafruit_MPU6050 mpu;
Adafruit_SSD1306 display = Adafruit_SSD1306(128, 64, &Wire);

// REPLACE WITH YOUR RECEIVER MAC Address
uint8_t broadcastAddress[] = {0x0C, 0xB8, 0x15, 0xB9, 0x86, 0xB8};

// Structure example to send data
typedef struct struct_message {
    float x;
    float y;
} struct_message;

struct_message myData;
esp_now_peer_info_t peerInfo;

void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
    Serial.print("\r\nLast Packet Send Status:\t");
    Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}
    
void setup() {
    Serial.begin(115200);
    Serial.println("MPU6050 OLED demo");

    if (!mpu.begin()) {
        Serial.println("Sensor init failed");
        while (1) yield();
    }
    
    WiFi.mode(WIFI_STA);

    if (esp_now_init() != ESP_OK) {
        Serial.println("Error initializing ESP-NOW");
        return;
    }

    esp_now_register_send_cb(OnDataSent);
    
    memcpy(peerInfo.peer_addr, broadcastAddress, 6);
    peerInfo.channel = 0;  
    peerInfo.encrypt = false;
    
    if (esp_now_add_peer(&peerInfo) != ESP_OK){
        Serial.println("Failed to add peer");
        return;
    }

    if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { 
        Serial.println(F("SSD1306 allocation failed"));
        for (;;);
    }
    display.display();
    delay(500); 
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setRotation(0);

    mpu.setAccelerometerRange(MPU6050_RANGE_16_G);
    mpu.setGyroRange(MPU6050_RANGE_250_DEG);
    mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);
}
    
void loop() {
    sensors_event_t a, g, temp;
    mpu.getEvent(&a, &g, &temp);
    
    myData.x = a.acceleration.x;
    myData.y = a.acceleration.y;

    display.clearDisplay();
    display.setCursor(0, 0);

    if (a.acceleration.x >= 4) {
        display.println("Back");
    } else if (a.acceleration.x <= -4) {
        display.println("Forward");
    } else if (a.acceleration.y <= -4) {
        display.println("Left");
    } else if (a.acceleration.y >= 4) {
        display.println("Right");
    } else {
        display.println("Stopped");
    }

    display.display();
    delay(100);
    
    esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
        
    if (result == ESP_OK) {
        Serial.println("Sent with success");
    } else {
        Serial.println("Error sending the data");
    }
    delay(100);
}
                    
Receiver Board Logic

#include <esp_now.h>
#include <WiFi.h>

const int backTrig = 13; const int frontTrig = 14;
const int leftTrig = 25; const int rightTrig = 32;

const int backEcho = 12; const int frontEcho = 27;
const int leftEcho = 33; const int rightEcho = 35;

const int A1A = 5;  const int A1B = 17;
const int B1A = 18; const int B1B = 19;

typedef struct struct_message {
    float x;
    float y;
} struct_message;

struct_message myData;

void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
    memcpy(&myData, incomingData, sizeof(myData));
}
    
void setup() {
    Serial.begin(115200);
    pinMode(A1A, OUTPUT); pinMode(A1B, OUTPUT);
    pinMode(B1A, OUTPUT); pinMode(B1B, OUTPUT);
    
    digitalWrite(A1A, 0); digitalWrite(A1B, 0);
    digitalWrite(B1A, 0); digitalWrite(B1B, 0);
    
    WiFi.mode(WIFI_STA);

    if (esp_now_init() != ESP_OK) {
        return;
    }
    esp_now_register_recv_cb(OnDataRecv);
}
    
void loop() { 
    bool was_back = false;

    if (myData.x >= 4) {
        was_back = true;
        while (myData.x >= 4) { backwards(); }
    } else if (myData.x <= -4) {
        was_back = false;
        while (myData.x <= -4) { forward(); }
    } else if (myData.y <= -4) {
        while (myData.y <= -4) { right(); }
    } else if (myData.y >= 4) {
        while (myData.y >= 4) { left(); }
    } else {
        stopped();
    }
}

// Logic helpers (abbreviated for display)
void forward() { digitalWrite(A1A, 1); digitalWrite(A1B, 0); digitalWrite(B1A, 1); digitalWrite(B1B, 0); }
void backwards() { digitalWrite(A1B, 1); digitalWrite(A1A, 0); digitalWrite(B1B, 1); digitalWrite(B1A, 0); }
void stopped() { digitalWrite(A1A, 0); digitalWrite(B1A, 0); digitalWrite(B1B, 0); digitalWrite(A1B, 0); }
void right() { digitalWrite(A1A, 1); digitalWrite(A1B, 0); digitalWrite(B1A, 0); digitalWrite(B1B, 0); }
void left() { digitalWrite(A1A, 0); digitalWrite(A1B, 0); digitalWrite(B1A, 1); digitalWrite(B1B, 0); }
                    

Resources & References