-
-
-
Tổng tiền thanh toán:
-
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"