The gesture control mechanism introduces ease of control of vehicles for specially-abled persons as well as for rescue purposes.
Three microcontrollers have been used for this project, one as a transmitter (ESP32), one as a receiver (Arduino Uno R3), and another just to transmit sensor data to a local web server (ESP32).
The transmitter will transmit hand movement data from MPU6050 (accelerometer and gyroscope sensor) through Bluetooth (HC05) and the receiver will receive that data to control 4 car wheels. A button has been added to the transmitter to control the headlight of the car(on the receiver).
For crash protection, two Ultra Sonar Sensor has been used, one on the front and another on the front, to detect and avoid objects.
The third ESP32 microcontroller reads temperature and humidity data from a DHT11 sensor and the rpm of the car wheel from an LM393 speed sensor. This data has been transmitted to a local server using the ESP32 in softAP mode through the Async Web Server library.
Specially-abled people cannot imagine driving a vehicle using all their limbs. This project opens the possibility for such people to control a car using only one or two hands. This also provides ease of control for others too.
Vehicles of such can also be used for standalone search and rescue purposes. Additional features can also be added for special missions. Further development is possible such as adding firefighting features, wireless cameras, artificial intelligence to collect and process data, etc.
ESP32 (2pc)
Arduino Uno R3
Four-wheel car chassis
Gear motor (4pc)
L293D motor driver shield
MPU6050 (Gyroscope and Accelerometer)
Ultrasonic sensor (2pc)
LM393 Speed Sensor
DHT11 (Temperature and Humidity Sensor)
16x2 I2C LCD
0.96" I2C OLED
Green and Red LED
3.7V battery (3pc)
Battery holder
Battery charger
PCB for transmitter
Code for Transmitter
/*
--------------------------------------------------------------
Transmitter for Hand Gesture Controlled Car with Crash Protection
01.08.2023
S. M. ASHIK AL MASHRAFI
--------------------------------------------------------------
*/
#include<BluetoothSerial.h>
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif
#include <Adafruit_MPU6050.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
#define LED 5
#define button 4
int buttonState;
int buttonNew;
int buttonOld;
int i=0;
char gyroData;
char buttonData;
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
Adafruit_MPU6050 mpu;
BluetoothSerial espBT;
uint8_t ADDR[6] = {0x00, 0x08, 0x1B, 0x8A, 0x82, 0x4B};
String name = "ASHIKHC06";
char *pin = "1234";
bool connected;
char charData;
int intData;
void setup(void) {
Serial.begin(9600);
pinMode(LED, OUTPUT);
pinMode(button, INPUT);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
espBT.begin("Gyro", true);
espBT.setPin(pin);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.println("ESP32 Started in\nmaster mode");
display.println("\nConnecting...");
display.display();
connected = espBT.connect(ADDR);
if(connected){
display.println("\nConnected\nsuccessfully!");
digitalWrite(LED, HIGH);
display.display();
delay(500);
}
else{
display.print("\nError: Could not\nconnect!");
display.display();
delay(500);
}
mpu.begin();
mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
mpu.setGyroRange(MPU6050_RANGE_500_DEG);
mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);
}
void loop() {
sensors_event_t a, g, temp;
mpu.getEvent(&a, &g, &temp);
int x = a.acceleration.x;
int y = a.acceleration.y;
gyroData = sendtoBT(x , y);
buttonNew = digitalRead(button);
if(buttonOld==0 && buttonNew==1){
if (buttonState==0){
buttonState=1;
espBT.write('1');
buttonData = '1';
}
else{
buttonState=0;
espBT.write('0');
buttonData = '0';
}
}
buttonOld=buttonNew;
displayOLED(x, y, gyroData, buttonData, charData);
delay(200);
}
char sendtoBT(int x, int y){
if(y<-2){
espBT.write('l');
return 'l';
}
else if(y>6){
espBT.write('r');
return 'r';
}
else if(x>3){
espBT.write('b');
return 'b';
}
else if(x<-5){
espBT.write('f');
return 'f';
}
else{
espBT.write('s');
return 's';
}
}
void displayOLED(int x, int y, char gyroState, char buttonData, char charData){
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(32,3);
display.println("TRANSMITTER");
display.drawRect(0, 16, 128, 36, WHITE);
display.setCursor(5, 19);
display.print("x : ");
display.print(x);
display.drawLine(59, 16, 59, 28, WHITE);
display.setCursor(64, 19);
display.print("y : ");
display.print(y);
display.drawLine(0, 28, 128, 28, WHITE);
display.setCursor(5, 31);
if (gyroState == 's'){
display.print("Stopped");
}
else if (gyroState == 'f'){
display.print("Moving Forward");
}
else if (gyroState == 'b'){
display.print("Moving Backward");
}
else if (gyroState == 'l'){
display.print("Moving Left");
}
else if (gyroState == 'r'){
display.print("Moving Right");
}
display.drawLine(0, 40, 128, 40, WHITE);
display.setCursor(5, 43);
if (buttonData == '1'){
display.print("Headlight On");
}
else if (buttonData == '0'){
display.print("Headlight Off");
}
else{
display.print("Headlight Off");
}
display.setCursor(5, 1);
display.setTextSize(2);
display.write(31);
display.setCursor(112, 1);
display.write(31);
display.setTextSize(1);
display.setTextColor(BLACK, WHITE);
if(connected == HIGH){
display.setCursor(22, 55);
display.print(" BT connected ");
}
else{
display.setCursor(10, 55);
display.print(" BT not connected ");
}
display.display();
}
Code for Receiver
/*
--------------------------------------------------------------
Receiver for Hand Gesture Controlled Car with Crash Protection
10.08.2023
S. M. ASHIK AL MASHRAFI
©all rights reserved
--------------------------------------------------------------
*/
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x3F, 16, 2);
#include <AFMotor.h>
#define mototSpeed 150
AF_DCMotor fr(2);
AF_DCMotor fl(3);
AF_DCMotor br(1);
AF_DCMotor bl(4);
#include<NewPing.h>
#define echopinf A1
#define trigpinf A0
#define echopinb A3
#define trigpinb A2
NewPing sonarf(trigpinf, echopinf, 100);
NewPing sonarb(trigpinb, echopinb, 100);
int distancef;
int distanceb;
#include <SoftwareSerial.h>
SoftwareSerial mySerial(0, 1);
char data;
unsigned long previousMillis = 0;
const long interval = 500;
void setup(void) {
Serial.begin(9600);
while (!Serial) {
;
}
pinMode(2, OUTPUT);
digitalWrite(2, LOW);
mySerial.begin(9600);
lcd.init();
lcd.backlight();
fr.setSpeed(mototSpeed);
fl.setSpeed(mototSpeed);
br.setSpeed(mototSpeed);
bl.setSpeed(mototSpeed);
fr.run(RELEASE);
fl.run(RELEASE);
br.run(RELEASE);
bl.run(RELEASE);
}
void loop() {
distancef = sonarf.ping_cm();
distanceb = sonarb.ping_cm();
if (mySerial.available()) {
data = mySerial.read();
}
printLCD(distancef, distanceb, data);
if (data == '1'){
digitalWrite(2, HIGH);
}
else if (data == '0'){
digitalWrite(2, LOW);
}
if (data == 'f' && (distancef == 0 || distancef > 20)){
fr.run(FORWARD);
fl.run(FORWARD);
br.run(FORWARD);
bl.run(FORWARD);
}
else if (data == 'r'){
fr.run(BACKWARD);
fl.run(FORWARD);
br.run(BACKWARD);
bl.run(FORWARD);
}
else if (data == 'l'){
fr.run(FORWARD);
fl.run(BACKWARD);
br.run(FORWARD);
bl.run(BACKWARD);
}
else if (data == 'b' && (distanceb == 0 || distanceb > 20)){
fr.run(BACKWARD);
fl.run(BACKWARD);
br.run(BACKWARD);
bl.run(BACKWARD);
}
else{
fr.run(RELEASE);
fl.run(RELEASE);
br.run(RELEASE);
bl.run(RELEASE);
}
}
void printLCD(int distancef, int distanceb, char data){
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Df,cm:");
lcd.setCursor(6,0);
lcd.print(distancef);
lcd.setCursor(11,0);
lcd.print("BT:");
lcd.setCursor(15,0);
lcd.print(data);
lcd.setCursor(0,1);
lcd.print("Db,cm:");
lcd.setCursor(6,1);
lcd.print(distanceb);
}
}
Code for Web Interface
/*
--------------------------------------------------------------
Web Interface for Hand Gesture Controlled Car
10.08.2023
S. M. ASHIK AL MASHRAFI
©all rights reserved
--------------------------------------------------------------
*/
#include <WiFi.h>
#include <ESPAsyncWebSrv.h>
#include <DHT.h>
// DEFINE CONSTANTS ---
const int sensorPin = 4;
volatile unsigned long pulseCount = 0;
unsigned long prevTime = 0;
#define DHTPIN 2
#define DHTTYPE DHT11
#define StatusPin 19
#define ControlPin 18
const char *ssid = "Car Interface";
const char *password = "123";
// DEFINE DHT ---
DHT dht(DHTPIN, DHTTYPE);
void IRAM_ATTR handleInterrupt() {
pulseCount++;
}
String readSpeed() {
unsigned long currentTime = millis();
if (currentTime - prevTime >= 1000) {
detachInterrupt(digitalPinToInterrupt(sensorPin));
float frequency = pulseCount / 2.0;
float speed = (frequency * 60.0)*600 / 1000.0;
pulseCount = 0;
prevTime = currentTime;
attachInterrupt(digitalPinToInterrupt(sensorPin), handleInterrupt, RISING);
return String(speed);
}
}
// ASYNCRONOUS WEB SERVER STRUCTURE
//----------------------------------------------------------------------
AsyncWebServer server(80);
static const char PROGMEM index_html[] = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Hand Gesture Controlled Car Interface</title>
<style type="text/css">
*{
font-family: Arial;
}
body{
padding: 0;
margin: 0;
}
.header{
text-align: center;
font-size: 1.5rem;
font-weight: bold;
font-family: monospace;
padding: 15px 0;
color: #fff;
background-image: linear-gradient(to right, #393594 , #3386c7);
}
.subheader{
text-align: center;
font-weight: bold;
font-size: 1.2rem;
padding: 15px;
background: #eee;
}
.thsContainer{
margin: 30px 0 35px 0;
display: flex;
flex-direction: column;
align-items: center;
}
.thsContent{
width: 10rem;
height: 10rem;
text-align: center;
color: #fff;
border-radius: 100%;
margin: 10px;
}
.thsStat{
font-size: 1.5rem;
font-weight: bold;
margin: 5px 0px 3px 0;
}
.t{
background-image: linear-gradient(to right, #FE656A , #FFBB73);
}
.h{
background-image: linear-gradient(to right, #6EEBF8 , #15C1F7);
}
.s{
background-image: linear-gradient(to right, #AC59A2 , #B294B2);
}
.thsContent > div {
position: relative;
top: 27%;
}
.captionUpper{
margin: 0;
padding: 0;
font-size: 1.2rem;
opacity: 60%;
}
.captionLower{
margin: 0;
padding: 0;
font-size: 1.2rem;
font-weight: bold;
opacity: 50%;
}
.cp{
color: #ccc;
text-align: center;
margin-top: 25px;
font-family: monospace;
margin-bottom: 15px;
}
</style>
</head>
<body>
<div class="header">TEAM SPARK | RUET</div>
<div class="subheader">Hand Gesture Controlled Car Interface</div>
<div class="thsContainer">
<div class="thsContent s">
<div>
<p class="captionUpper">speed</p>
<p class="thsStat" id="speed">__</p>
<p class="captionLower">rpm</p>
</div>
</div>
<div class="thsContent t">
<div>
<p class="captionUpper">temperature</p>
<p class="thsStat" id="temperature">__</p>
<p class="captionLower">°C</p>
</div>
</div>
<div class="thsContent h">
<div>
<p class="captionUpper">humidity</p>
<p class="thsStat" id="humidity">__</p>
<p class="captionLower">%</p>
</div>
</div>
</div>
<div class="cp">
© S. M. Ashik Al Mashrafi
</div>
</body>
<script>
// VARIABLES ---
var temperature = document.getElementById("temperature");
var humidity = document.getElementById("humidity");
var speed = document.getElementById("speed");
// READ TEMPERATURE
setInterval(function ( ) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
temperature.innerHTML = this.responseText;
}
};
xhttp.open("GET", "/temperature", true);
xhttp.send();
}, 200);
// READ HUMIDITY ---
setInterval(function ( ) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
humidity.innerHTML = this.responseText;
}
};
xhttp.open("GET", "/humidity", true);
xhttp.send();
}, 200);
// READ SPEED ---
setInterval(function ( ) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
speed.innerHTML = this.responseText;
}
};
xhttp.open("GET", "/speed", true);
xhttp.send();
}, 200);
</script>
</html>
)rawliteral";
void setup() {
Serial.begin(115200);
pinMode(sensorPin, INPUT);
attachInterrupt(digitalPinToInterrupt(sensorPin), handleInterrupt, RISING);
// SET UP SAP MODE ---
WiFi.softAP(ssid);
IPAddress myIP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(myIP);
// SEND DATA TO LOCAL SERVER ---
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send_P(200, "text/html", index_html);
});
server.on("/humidity", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send_P(200, "text/plain", String(dht.readHumidity()).c_str());
});
server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send_P(200, "text/plain", String(dht.readTemperature()).c_str());
});
server.on("/speed", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send_P(200, "text/plain", readSpeed().c_str());
});
server.begin();
}
void loop() {
}
ESP32 (2pc)
Arduino Uno R3
Four-wheel car chassis
Gear motor (4pc)
L293D motor driver shield
MPU6050 (Gyroscope and Accelerometer)
Ultrasonic sensor (2pc)
LM393 Speed Sensor
DHT11 (Temperature and Humidity Sensor)
HC05 (Bluetooth Module)
16x2 I2C LCD
0.96" I2C OLED
Green and Red LED
3.7V battery (3pc)
Battery holder
Battery charger
PCB for transmitter
Wu, X., Su, M., & Wang, P. (2010). A hand-gesture-based control interface for a car-robot. https://doi.org/10.1109/iros.2010.5650294
Dutta, A. K., Paul, J. G., Siddharth, S., Nitish, K., Sundar, J., & Manoharan, P. (2023). Hand Gesture Controlled Car using Bluetooth Modules and Accelerometer Sensor. https://doi.org/10.1109/iccmc56507.2023.10083843
Hasan, R., Mim, A. K., Rashik, M. A., & Tahmid, I. A. (2016). Hand Gesture Based Car Control. https://cse.buet.ac.bd/common/Project/B1/IFP_2_B1/project.htm#howitworks