Xe Robot điều khiển bằng điện thoại qua wifi với ESP32

23/02/2026

Xe Robot điều khiển bằng điện thoại qua wifi với ESP32

Giao diện:

  • Bên phải: 4 nút tròn (Tiến, Lùi, Trái, Phải)

  • Bên trái: 6 nút (2 cột) chọn mức tốc độ tối đa

  • Giữa: chữ GPC-CAR

  • Giữa dưới:

    • Còi

    • 2 nút Xi nhan trái / phải

    • đèn pha/cốt

  • Truy cập 

    wifi "GPC-CAR"; password = "12345678";

  • Vào trình duyệt web gõ: 192.168.4.1 để mở giao diện điều khiển

1. Sơ đồ chân kết nối

Chức năng GPIO
Motor ENA 14
Motor IN1 27
Motor IN2 26
Motor ENB 12
Motor IN3 25
Motor IN4 33
Servo 18
Horn 4
Light Low 16
Light High 17
Signal Left 5
Signal Right 19

a. Kết nối L298N với ESP32

Điều khiển động cơ trái

L298N ESP32
ENA GPIO14
IN1 GPIO27
IN2 GPIO26

Điều khiển động cơ phải

L298N ESP32
ENB GPIO12
IN3 GPIO25
IN4 GPIO33

b. Kết nối còi, đèn, xi nhan

Thiết bị ESP32
Còi GPIO13
Xi nhan trái GPIO4
Xi nhan phải GPIO16
Đèn pha GPIO17
Đèn cốt GPIO5

c. Cấp nguồn L298N

L298N Kết nối
12V Pin +
GND Pin -
5V KHÔNG dùng (nếu dùng nguồn ngoài)

Quan trọng:

✔ Nối GND L298N với GND ESP32


d. Kết nối động cơ

L298N Động cơ
OUT1 OUT2 Motor trái
OUT3 OUT4 Motor phải

e. Sơ đồ tổng thể dạng hình 

ESP32 L298N

GPIO27 -------- IN1
GPIO26 -------- IN2
GPIO25 -------- IN3
GPIO33 -------- IN4

GPIO14 -------- ENA
GPIO12 -------- ENB

GND -------- GND


Motor trái ---- OUT1 OUT2
Motor phải ---- OUT3 OUT4

Với xe robot của bạn hiện tại dùng:

Motor: 6 chân
Còi: 1
Đèn: 4

Tổng: 11 chân

→ ESP32 vẫn dư nhiều chân

f. Mapping phím:

Phím Chức năng
↑ ↓ ← → Di chuyển
Space Dừng
W Còi
A Xi nhan trái
S Xi nhan phải
R Đèn pha
F Đèn cốt
1 Tốc độ S1
2 Tốc độ S2
3 Tốc độ S3
4 Tốc độ S4
5 Tốc độ S5
6 Tốc độ S6

2. Code ESP32 hoàn chỉnh 

#include <WiFi.h>

#include <WebServer.h>

const char* ssid = "GPC-CAR";

const char* password = "12345678";

WebServer server(80);

// MOTOR

#define IN1 27

#define IN2 26

#define IN3 25

#define IN4 33

#define ENA 14

#define ENB 12


 

// DEVICES

 

#define HORN 13

#define LEFT_LED 4

#define RIGHT_LED 16

#define HIGH_LED 17

#define LOW_LED 5

int speedVal = 200;

// HTML

String html = R"rawliteral(

<!DOCTYPE html>

<html>

<head>

<meta name="viewport" content="width=device-width, initial-scale=1">

<style>

body{

background:black;

color:white;

font-family:Arial;

overflow:hidden;

}


 

/* TITLE */

.title{

position:absolute;

top:20px;

left:50%;

transform:translateX(-50%);

font-size:40px;

}

.speedView{

position:absolute;

top:70px;

left:50%;

transform:translateX(-50%);

font-size:25px;

}

/* SPEED PANEL LEFT */

.speedPanel{

position:absolute;

left:20px;

top:50%;

transform:translateY(-50%);

display:grid;

grid-template-columns:70px 70px;

gap:15px;

}

.speedPanel button{

height:55px;

font-size:22px;

background:#222;

color:white;

border:none;

border-radius:12px;

box-shadow:0 0 15px #00AEEF;

}

.speedPanel button:active{

background:#00AEEF;

}

/* CENTER PANEL */

.centerPanel{

position:absolute;

left:50%;

top:55%;

transform:translate(-50%,-50%);

text-align:center;

}

.row{

margin:15px;

}

.centerPanel button{

width:110px;

height:50px;

margin:5px;

font-size:18px;

background:#222;

color:white;

border:none;

border-radius:12px;

box-shadow:0 0 15px #00AEEF;

}

.centerPanel button:active{

background:#00AEEF;

}

/* DPAD RIGHT */


.dpad{

position:absolute;

right:40px;

top:50%;

transform:translateY(-50%);

width:200px;

height:200px;

}

.btn{

position:absolute;

width:75px;

height:75px;

border-radius:50%;

background:#00AEEF;

border:none;

box-shadow:0 0 20px #00AEEF;

}


.btn:active{

background:#0077AA;

box-shadow:

0 0 40px white,

0 0 80px #00AEEF;

transform:scale(0.9);

}

.up{top:0; left:65px;}

.down{bottom:0; left:65px;}

.left{left:0; top:65px;}

.right{right:0; top:65px;}

/* ARROW */

.arrow::after{

content:"";

position:absolute;

left:50%;

top:50%;

transform:translate(-50%,-50%);

}

.up::after{

border-left:15px solid transparent;

border-right:15px solid transparent;

border-bottom:25px solid white;

}


 

.down::after{

border-left:15px solid transparent;

border-right:15px solid transparent;

border-top:25px solid white;

}

.left::after{

border-top:15px solid transparent;

border-bottom:15px solid transparent;

border-right:25px solid white;

}

.right::after{

border-top:15px solid transparent;

border-bottom:15px solid transparent;

border-left:25px solid white;

}


</style>

</head>

<body>

<div class="title">

GPC-CAR

</div>

<div class="speedView">

Speed: <span id="speed">200</span>

</div>

<!-- SPEED LEFT -->

<div class="speedPanel">

 

<button onclick="setSpeed(80)">1</button>

<button onclick="setSpeed(120)">2</button>

<button onclick="setSpeed(160)">3</button>

<button onclick="setSpeed(200)">4</button>

<button onclick="setSpeed(230)">5</button>

<button onclick="setSpeed(255)">6</button>

</div>

<!-- CENTER -->

<div class="centerPanel">

<div class="row">

<button onclick="horn()">HORN</button>

</div>

<div class="row">

<button onclick="leftSignal()">LEFT</button>

 

<button onclick="rightSignal()">RIGHT</button>

 

</div>

<div class="row">

<button onclick="highLight()">HIGH</button>

<button onclick="lowLight()">LOW</button>

</div>

</div>

<!-- DPAD RIGHT -->

<div class="dpad">

<button class="btn arrow up"

onmousedown="move('f')"

onmouseup="stop()"

onmouseleave="stop()"

ontouchstart="move('f')"

ontouchend="stop()">

</button>

<button class="btn arrow down"

onmousedown="move('b')"

onmouseup="stop()"

onmouseleave="stop()"

 

ontouchstart="move('b')"

ontouchend="stop()">

</button>

<button class="btn arrow left"

onmousedown="move('l')"

onmouseup="stop()"

onmouseleave="stop()"

 

ontouchstart="move('l')"

ontouchend="stop()">

 

</button>



 

<button class="btn arrow right"

 

onmousedown="move('r')"

onmouseup="stop()"

onmouseleave="stop()"

 

ontouchstart="move('r')"

ontouchend="stop()">

 

</button>


 

</div>




 

<script>


 

function move(d){

 

fetch("/move?dir="+d);

}

function stop(){

fetch("/stop");

}

function setSpeed(s){

fetch("/speed?val="+s);

document.getElementById("speed").innerHTML=s;

}


function horn(){

fetch("/horn");

}

function leftSignal(){

fetch("/left");

}

function rightSignal(){

fetch("/right");

}

function highLight(){

fetch("/high");

}

function lowLight(){

fetch("/low");

}
 

/* KEYBOARD */

document.addEventListener("keydown", function(e){

if(e.repeat) return;

if(e.key=="ArrowUp") move('f');

if(e.key=="ArrowDown") move('b');

if(e.key=="ArrowLeft") move('l');

if(e.key=="ArrowRight") move('r');

if(e.key=="w") horn();

if(e.key=="a") leftSignal();

if(e.key=="s") rightSignal();

if(e.key=="r") highLight();

if(e.key=="f") lowLight();

if(e.key=="1") setSpeed(80);

if(e.key=="2") setSpeed(120);

if(e.key=="3") setSpeed(160);

if(e.key=="4") setSpeed(200);

if(e.key=="5") setSpeed(230);

if(e.key=="6") setSpeed(255);

});

document.addEventListener("keyup", function(e){

stop();

});

</script>

</body>

</html>

)rawliteral";

// MOTOR CONTROL

void forward(){

analogWrite(ENA,speedVal);

analogWrite(ENB,speedVal);

digitalWrite(IN1,HIGH);

digitalWrite(IN2,LOW);

digitalWrite(IN3,HIGH);

digitalWrite(IN4,LOW);

}

void back(){

analogWrite(ENA,speedVal);

analogWrite(ENB,speedVal);

digitalWrite(IN1,LOW);

digitalWrite(IN2,HIGH);

digitalWrite(IN3,LOW);

digitalWrite(IN4,HIGH);

}

void left(){

analogWrite(ENA,speedVal);

analogWrite(ENB,speedVal);

digitalWrite(IN1,LOW);

digitalWrite(IN2,HIGH);

digitalWrite(IN3,HIGH);

digitalWrite(IN4,LOW);

}

void right(){

analogWrite(ENA,speedVal);

analogWrite(ENB,speedVal);

digitalWrite(IN1,HIGH);

digitalWrite(IN2,LOW);

digitalWrite(IN3,LOW);

digitalWrite(IN4,HIGH);

}

void stopCar(){

digitalWrite(IN1,LOW);

digitalWrite(IN2,LOW);

digitalWrite(IN3,LOW);

digitalWrite(IN4,LOW);

}

// SETUP

void setup(){

pinMode(IN1,OUTPUT);

pinMode(IN2,OUTPUT);

pinMode(IN3,OUTPUT);

pinMode(IN4,OUTPUT);

pinMode(ENA,OUTPUT);

pinMode(ENB,OUTPUT);

pinMode(HORN,OUTPUT);

pinMode(LEFT_LED,OUTPUT);

pinMode(RIGHT_LED,OUTPUT);

pinMode(HIGH_LED,OUTPUT);

pinMode(LOW_LED,OUTPUT);

 

WiFi.softAP(ssid,password);

server.on("/",[](){

server.send(200,"text/html",html);

});

server.on("/move",[](){

String d=server.arg("dir");

if(d=="f") forward();

if(d=="b") back();

if(d=="l") left();

if(d=="r") right();

server.send(200,"text/plain","OK");

});

server.on("/stop",[](){

stopCar();

});

server.on("/speed",[](){

speedVal=server.arg("val").toInt();

});

server.on("/horn",[](){

digitalWrite(HORN,!digitalRead(HORN));

});

server.on("/left",[](){

digitalWrite(LEFT_LED,!digitalRead(LEFT_LED));

});

server.on("/right",[](){

digitalWrite(RIGHT_LED,!digitalRead(RIGHT_LED));

});

server.on("/high",[](){

digitalWrite(HIGH_LED,!digitalRead(HIGH_LED));

});

server.on("/low",[](){

digitalWrite(LOW_LED,!digitalRead(LOW_LED));

});

server.begin();

}

void loop(){

server.handleClient();

}

 

4. Tính năng

✔ Giữ nút chạy – thả dừng
✔ 6 mức tốc độ + hiển thị
✔ 4 nút điều hướng hình tròn
✔ Xi nhan trái / phải
✔ Còi
✔ Đèn PHA
✔ Đèn CỐT
✔ Hiệu ứng nút sáng

✔ Điều khiển bằng Web + Touch + Chuột + Bàn phím
✔ Phím mũi tên → di chuyển
✔ W → Còi (giữ kêu, thả tắt)
✔ A → Xi nhan trái
✔ S → Xi nhan phải
✔ R → Đèn pha
✔ F → Đèn cốt
✔ 6 mức tốc độ
✔ Giữ nút chạy, thả dừng
✔ Giao diện GPC-CAR


5. Nếu muốn tôi có thể nâng cấp thêm kiểu xe thật:

✔ Công tắc 3 chế độ

  • OFF

  • CỐT

  • PHA

✔ Tự tắt khi dừng xe

✔ Flash pha xin vượt

✔ Icon đèn xe đẹp

Chỉ cần nói:

"Làm hệ thống đèn xe như ô tô thật cho ESP32"