ESP32 điều khiển động cơ DC với driver L298N: đảo chiều, chỉnh tốc độ bằng PWM
- 1. Vì sao không nối động cơ thẳng vào ESP32
- 2. L298N và cầu H
- 3. Linh kiện cần chuẩn bị
- 4. Sơ đồ nối dây
- 5. Chương trình 1: tăng tốc, phanh, lùi, thả trôi
- 6. Chương trình 2: điều khiển bằng lệnh trên Serial Monitor
- 7. Giải thích các lệnh chính
- 8. Chọn tần số PWM cho L298N
- 9. Gợi ý dùng trong giờ học STEM
- 10. Xử lý lỗi thường gặp
- 11. Xem thêm
Hướng dẫn nối ESP32 với driver L298N và động cơ DC giảm tốc: cầu H, đảo chiều, phanh, chỉnh tốc độ bằng PWM, điều khiển bằng lệnh trên Serial Monitor và thí nghiệm STEM.
Động cơ DC giảm tốc là bộ phận quen thuộc trong xe robot, băng tải mini và các mô hình STEM. Chân GPIO của ESP32 chỉ cấp được vài chục miliampe ở 3,3 V, trong khi động cơ cần hàng trăm miliampe ở 6–12 V. Vì vậy cần một driver động cơ đứng giữa. Module L298N là driver phổ biến nhất trong các bộ kit học tập.
Bài viết giải thích cầu H bên trong L298N, cách nối ESP32 với module và động cơ, hai chương trình mẫu (tự chạy tiến, lùi, phanh; điều khiển bằng lệnh gõ trên Serial Monitor) và gợi ý thí nghiệm cho giờ học STEM.
1. Vì sao không nối động cơ thẳng vào ESP32
- Dòng điện: động cơ giảm tốc loại nhỏ cần khoảng 100–250 mA khi chạy không tải và lớn hơn nhiều lúc khởi động. Chân GPIO của ESP32 chỉ nên cấp vài chục miliampe.
- Điện áp: GPIO chỉ có 3,3 V, trong khi động cơ thường chạy ở 3–12 V.
- Đảo chiều: muốn quay ngược phải đổi cực điện áp trên hai đầu động cơ, việc này cần một cầu H.
- Sức điện động ngược: khi ngắt điện, cuộn dây động cơ sinh xung điện áp ngược có thể làm hỏng chân vi điều khiển.
2. L298N và cầu H

Mỗi kênh của L298N là một cầu H gồm bốn transistor xếp thành chữ H, động cơ nằm ở thanh ngang. Đóng cặp chéo S1–S4 thì dòng điện chạy qua động cơ theo một chiều, đóng cặp S2–S3 thì chạy theo chiều ngược lại. Module có hai kênh nên điều khiển được hai động cơ DC.
- IN1, IN2 chọn chiều quay của kênh A (OUT1, OUT2). IN3, IN4 làm việc tương tự cho kênh B (OUT3, OUT4).
- ENA bật hoặc tắt kênh A. Cấp xung PWM vào ENA thì chỉnh được tốc độ. Muốn dùng PWM phải rút jumper đang cắm trên chân ENA.
- Phanh và thả trôi: khi ENA ở mức cao và IN1 bằng IN2, hai đầu động cơ bị nối với nhau, động cơ dừng nhanh. Khi ENA ở mức thấp, động cơ bị ngắt điện và quay trơn rồi mới dừng.
| Thông số | Giá trị |
|---|---|
| Số kênh | 2 kênh cầu H (2 động cơ DC hoặc 1 động cơ bước hai pha) |
| Dòng mỗi kênh | 2 A liên tục, cần tản nhiệt khi chạy lâu |
| Sụt áp trên driver | khoảng 1,8–3,2 V ở 1 A (transistor lưỡng cực) |
| Mức logic | ngưỡng mức cao 2,3 V, nên nhận được tín hiệu 3,3 V của ESP32 |
| Ổn áp 5 V trên module | dùng khi giữ jumper 5V và nguồn động cơ không quá 12 V |
Vì sụt áp lớn, với nguồn 7,4 V từ hai pin 18650 thì động cơ chỉ nhận khoảng 5–5,5 V khi chạy hết tốc độ. Như vậy vừa với động cơ giảm tốc 3–6 V trong bộ kit.
3. Linh kiện cần chuẩn bị
- ESP32 DevKit V1 30 chân (hoặc loại chip nạp CH340).
- Module driver L298N.
- Động cơ DC giảm tốc 3–6 V (loại vỏ vàng hay dùng cho xe robot).
- Khay pin 18650 loại 2 viên nối tiếp (2S) và hai pin 18650.
- Dây cắm test board, tua vít nhỏ để siết cọc vít.
4. Sơ đồ nối dây

| L298N | Nối tới | Ghi chú |
|---|---|---|
| ENA | D27 (GPIO27) | Rút jumper ENA trước khi nối |
| IN1 | D26 (GPIO26) | |
| IN2 | D25 (GPIO25) | |
| GND | GND của ESP32 và cực − của pin | Bắt buộc nối chung |
| 12V | Cực + của khay pin | Dùng được nguồn 6–12 V |
| OUT1, OUT2 | Hai chân động cơ | Đổi chỗ hai dây thì đổi chiều quay |
- Khi làm thí nghiệm, cấp nguồn cho ESP32 qua cổng USB của máy tính để xem Serial Monitor.
- Khi lắp lên xe chạy pin, có thể lấy 5 V từ cọc 5V của L298N (giữ jumper 5V) cấp vào chân VIN của ESP32. Ổn định hơn là dùng mạch hạ áp LM2596 hoặc MP1584. Không cắm USB và cấp VIN từ nguồn ngoài cùng lúc.
5. Chương trình 1: tăng tốc, phanh, lùi, thả trôi
Chương trình tự chạy một vòng: tăng tốc từ 0 lên 100 % trong khoảng 3 giây, phanh, lùi 60 %, rồi thả trôi. Gói esp32 phiên bản 3.x có sẵn các hàm PWM, không cần cài thêm thư viện.
/*
PYWORLD.VN - ESP32 điều khiển động cơ DC qua L298N: tăng tốc, phanh, lùi, thả trôi
L298N: ENA -> GPIO27 (PWM, đã bỏ jumper ENA), IN1 -> GPIO26, IN2 -> GPIO25, GND L298N -> GND ESP32
Gói esp32 phiên bản 3.x, không cần cài thêm thư viện
*/
#define ENA_PIN 27 // Chân PWM chỉnh tốc độ
#define IN1_PIN 26 // Hai chân chọn chiều quay
#define IN2_PIN 25
#define PWM_TAN_SO 1000 // Tần số PWM (Hz)
#define PWM_BIT 10 // Độ phân giải 10 bit: duty từ 0 đến 1023
const uint32_t DUTY_MAX = (1 << PWM_BIT) - 1;
// tocDo từ -100 đến 100 (%): dương là tiến, âm là lùi, 0 là thả trôi
void quay(int tocDo) {
tocDo = constrain(tocDo, -100, 100);
if (tocDo > 0) {
digitalWrite(IN1_PIN, HIGH);
digitalWrite(IN2_PIN, LOW);
} else if (tocDo < 0) {
digitalWrite(IN1_PIN, LOW);
digitalWrite(IN2_PIN, HIGH);
}
// tocDo = 0: ENA ở mức thấp, hai đầu ra bị ngắt, động cơ quay trơn rồi tự dừng
ledcWrite(ENA_PIN, (uint32_t)abs(tocDo) * DUTY_MAX / 100);
}
// Phanh: hai đầu động cơ cùng nối về 0 V, động cơ dừng nhanh
void phanh() {
digitalWrite(IN1_PIN, LOW);
digitalWrite(IN2_PIN, LOW);
ledcWrite(ENA_PIN, DUTY_MAX);
}
void setup() {
Serial.begin(115200);
pinMode(IN1_PIN, OUTPUT);
pinMode(IN2_PIN, OUTPUT);
if (!ledcAttach(ENA_PIN, PWM_TAN_SO, PWM_BIT)) {
Serial.println("Khong cau hinh duoc PWM tren chan ENA!");
while (true) delay(1000);
}
quay(0);
Serial.println("ESP32 + L298N: bat dau sau 2 giay");
delay(2000);
}
void loop() {
Serial.println("Tien: tang toc tu 0 den 100 %");
for (int v = 0; v <= 100; v += 10) {
quay(v);
Serial.printf(" toc do %3d %%\n", v);
delay(300);
}
delay(2000);
Serial.println("Phanh");
phanh();
delay(1000);
Serial.println("Lui 60 %");
quay(-60);
delay(3000);
Serial.println("Tha troi (ngat dien, dong co tu dung)");
quay(0);
delay(3000);
}
6. Chương trình 2: điều khiển bằng lệnh trên Serial Monitor
Chương trình thứ hai cho phép gõ lệnh để đổi chiều, tốc độ và tần số PWM. Đây là cách tiện để làm thí nghiệm mà không cần thêm nút nhấn hay biến trở. Trong Serial Monitor chọn tốc độ 115200 baud và kiểu xuống dòng "New Line".
| Lệnh | Tác dụng |
|---|---|
t 50 | Tiến với 50 % tốc độ (0–100) |
l 30 | Lùi với 30 % tốc độ; nếu đang tiến thì phanh 0,2 giây trước khi đổi chiều |
d | Thả trôi |
p | Phanh |
f 5000 | Đổi tần số PWM (100–20000 Hz) |
q | Quét duty từ 0 lên 100 %, mỗi bước 5 %, giữ 1 giây |
/*
PYWORLD.VN - Điều khiển động cơ DC bằng lệnh gõ trên Serial Monitor (ESP32 + L298N)
Lệnh: t <0-100> tiến, l <0-100> lùi, d thả trôi, p phanh, f <100-20000> đổi tần số PWM, q quét duty
Nối dây như chương trình DC_L298N_TIEN_LUI; Serial Monitor chọn 115200 baud và "New Line"
*/
#define ENA_PIN 27
#define IN1_PIN 26
#define IN2_PIN 25
#define PWM_BIT 10
const uint32_t DUTY_MAX = (1 << PWM_BIT) - 1;
uint32_t tanSo = 1000; // Tần số PWM hiện tại (Hz)
int tocDoHienTai = 0; // -100..100 %
void quay(int tocDo) {
tocDo = constrain(tocDo, -100, 100);
if (tocDo > 0) {
digitalWrite(IN1_PIN, HIGH);
digitalWrite(IN2_PIN, LOW);
} else if (tocDo < 0) {
digitalWrite(IN1_PIN, LOW);
digitalWrite(IN2_PIN, HIGH);
}
ledcWrite(ENA_PIN, (uint32_t)abs(tocDo) * DUTY_MAX / 100);
}
void phanh() {
digitalWrite(IN1_PIN, LOW);
digitalWrite(IN2_PIN, LOW);
ledcWrite(ENA_PIN, DUTY_MAX);
}
// Đổi tốc độ; nếu đổi chiều thì phanh 0,2 giây trước để dòng điện không tăng vọt
void datTocDo(int moi) {
moi = constrain(moi, -100, 100);
if ((moi > 0 && tocDoHienTai < 0) || (moi < 0 && tocDoHienTai > 0)) {
phanh();
delay(200);
}
quay(moi);
tocDoHienTai = moi;
}
// Thí nghiệm: tăng duty từng bước 5 % để tìm mức nhỏ nhất làm động cơ bắt đầu quay
void quetDuty() {
Serial.println("Quet duty: 0 -> 100 %, moi buoc 5 %, giu 1 giay");
for (int v = 0; v <= 100; v += 5) {
datTocDo(v);
Serial.printf(" duty %3d %% (gia tri %4u / %u)\n", v, (unsigned)(v * DUTY_MAX / 100), (unsigned)DUTY_MAX);
delay(1000);
}
datTocDo(0);
Serial.println("Xong. Ghi lai duty nho nhat lam dong co bat dau quay.");
}
void inHuongDan() {
Serial.println("Lenh: t 50 = tien 50 %, l 30 = lui 30 %, d = tha troi, p = phanh,");
Serial.println(" f 5000 = doi tan so PWM, q = quet duty tu 0 den 100 %");
}
void xuLyLenh(String lenh) {
lenh.trim();
if (lenh.length() == 0) return;
char c = tolower(lenh.charAt(0));
int so = lenh.substring(1).toInt();
switch (c) {
case 't': datTocDo(so); break;
case 'l': datTocDo(-so); break;
case 'd': datTocDo(0); break;
case 'p': phanh(); tocDoHienTai = 0; break;
case 'f':
if (so < 100 || so > 20000) {
Serial.println("Tan so nen trong khoang 100 - 20000 Hz");
return;
}
tanSo = ledcChangeFrequency(ENA_PIN, so, PWM_BIT);
quay(tocDoHienTai); // ghi lại duty sau khi đổi tần số
break;
case 'q': quetDuty(); return;
default:
Serial.println("Lenh khong hop le");
inHuongDan();
return;
}
Serial.printf("Toc do: %d %% | tan so PWM: %u Hz\n", tocDoHienTai, (unsigned)tanSo);
}
void setup() {
Serial.begin(115200);
pinMode(IN1_PIN, OUTPUT);
pinMode(IN2_PIN, OUTPUT);
ledcAttach(ENA_PIN, tanSo, PWM_BIT);
quay(0);
delay(500);
Serial.println("ESP32 + L298N: dieu khien dong co bang lenh");
inHuongDan();
}
void loop() {
if (Serial.available()) {
xuLyLenh(Serial.readStringUntil('\n'));
}
}

7. Giải thích các lệnh chính
ledcAttach(ENA_PIN, 1000, 10): gắn chân ENA vào bộ PWM của ESP32 với tần số 1000 Hz, độ phân giải 10 bit (duty 0–1023).ledcWrite(ENA_PIN, duty): đặt độ rộng xung. Phần trăm tốc độ được đổi ra giá trị duty theo công thứcphần trăm × 1023 / 100.digitalWrite(IN1_PIN, ...),digitalWrite(IN2_PIN, ...): chọn chiều quay theo bảng trạng thái ở mục 2.phanh(): đặt IN1 = IN2 = 0 và ENA ở mức cao. Hàmquay(0)chỉ đưa ENA về 0, nên động cơ thả trôi.ledcChangeFrequency(): đổi tần số PWM khi chương trình đang chạy, rồi ghi lại duty.- Đổi chiều đột ngột ở tốc độ cao làm dòng điện tăng vọt. Chương trình phanh 0,2 giây trước khi đổi chiều để giảm dòng.
8. Chọn tần số PWM cho L298N
- Tần số thấp (vài trăm Hz) cho mô-men tốt ở tốc độ chậm nhưng động cơ rung và kêu ù.
- Khoảng 1–5 kHz là mức thường dùng với L298N: động cơ chạy êm vừa phải, driver không nóng thêm nhiều.
- Trên 20 kHz thì tai người không còn nghe tiếng rít. Tuy vậy L298N chuyển mạch chậm, ở tần số cao driver nóng hơn và tốc độ thấp kém đi.
Lệnh f trong chương trình 2 cho phép tự nghe và so sánh các tần số này.
9. Gợi ý dùng trong giờ học STEM
Mục tiêu: học sinh hiểu vì sao động cơ cần driver, cầu H đổi chiều quay thế nào, và điện áp trung bình của PWM quyết định tốc độ ra sao.
- Tìm duty khởi động. Gõ lệnh
qvà ghi lại duty nhỏ nhất làm động cơ bắt đầu quay. Với L298N và động cơ giảm tốc nhỏ, duty này thường khá cao vì driver sụt áp và động cơ cần đủ lực để thắng ma sát. Thảo luận: tại sao dưới mức đó động cơ chỉ kêu mà không quay? - Đo tốc độ theo duty. Dán một mẩu băng dính lên bánh xe, đặt duty 40, 60, 80, 100 %, đếm số vòng trong 10 giây. Vẽ đồ thị số vòng mỗi phút theo duty và nhận xét đồ thị có thẳng không.
- So sánh phanh và thả trôi. Cho bánh xe quay 100 %, lần lượt dùng lệnh
pvàd, quan sát bánh xe quay thêm bao lâu mới dừng. Giải thích bằng hình cầu H ở mục 2. - Đo sụt áp trên driver. Dùng đồng hồ vạn năng đo điện áp pin và điện áp giữa OUT1, OUT2 ở 100 %. Hiệu hai số là phần điện áp L298N giữ lại.
Các thí nghiệm này gắn với kiến thức về điện và từ trong môn Khoa học tự nhiên, Vật lý (động cơ điện một chiều, công suất), kỹ năng lập bảng và vẽ đồ thị trong Toán, và lập trình trong Tin học.
10. Xử lý lỗi thường gặp
| Hiện tượng | Nguyên nhân thường gặp | Cách xử lý |
|---|---|---|
| Động cơ không quay, đèn module sáng | Chưa nối GND chung; jumper ENA còn cắm nhưng chân ENA lại nối GPIO; duty quá thấp | Nối GND của ESP32 với GND của L298N; rút jumper ENA; thử t 100 |
| Chỉ quay được một chiều | Lỏng dây IN1 hoặc IN2 | Kiểm tra lại hai dây chọn chiều |
| ESP32 khởi động lại khi động cơ chạy | Nguồn sụt khi động cơ khởi động, nhiễu từ động cơ | Dùng pin còn đầy; cấp ESP32 bằng nguồn riêng hoặc mạch hạ áp; hàn tụ 100 nF giữa hai chân động cơ |
| Tốc độ thấp không quay, chỉ kêu | Duty dưới mức khởi động | Đặt duty tối thiểu cao hơn mức tìm được ở thí nghiệm 1 |
| Module nóng | Động cơ kéo dòng lớn hoặc bị kẹt | Giảm tải; cho nghỉ; dùng driver MOSFET khi cần dòng lớn |
| Động cơ kêu rít | Tần số PWM nằm trong vùng nghe được | Thử f 2000 đến f 5000 và chọn mức êm nhất |
11. Xem thêm
- PWM trên ESP32: điều chỉnh độ sáng LED bằng analogWrite và LEDC
- Mạch hạ áp LM2596 và MP1584: cách chọn, chỉnh điện áp và cấp nguồn ESP32
- Đọc ADC trên ESP32 và đo điện áp pin 18650
- Mức logic 3,3 V và 5 V: khi nào cần chuyển mức
Các bài tiếp theo trong loạt ESP32 điều khiển động cơ: đo tốc độ bằng encoder, giữ tốc độ ổn định bằng PID và xe robot điều khiển qua Wi-Fi.
Xem thêm