Hướng dẫn ESP32 làm web server điều khiển relay 4 kênh qua WiFi
- 1. Cách hoạt động
- 2. Module relay và mức kích
- 3. Linh kiện cần chuẩn bị
- 4. Sơ đồ nối dây
- 5. Cài đặt Arduino IDE
- 6. Chương trình web server điều khiển relay
- 7. Tìm địa chỉ IP và mở trang điều khiển
- 8. Giải thích các lệnh chính
- 9. Giữ relay tắt khi khởi động
- 10. Giới hạn bảo mật và an toàn điện
- 11. Xử lý lỗi thường gặp
Hướng dẫn dùng ESP32 DevKit V1 làm web server trong mạng WiFi nội bộ, mở trang trên điện thoại để bật, tắt module relay 4 kênh; có sơ đồ nối dây, code Arduino IDE và lưu ý an toàn.
ESP32 có sẵn WiFi nên có thể tự làm một web server nhỏ: mở trình duyệt trên điện thoại, vào địa chỉ IP của ESP32 rồi bấm nút để đóng, cắt relay. Bài viết hướng dẫn nối module relay 4 kênh với ESP32 DevKit V1, nạp chương trình bằng Arduino IDE và mở trang điều khiển có nút BẬT, TẮT cho từng kênh. Đây là phần tiếp theo của bài ESP32 điều khiển module relay 4 kênh an toàn, nơi đã trình bày cách đấu nối và an toàn cơ bản.
1. Cách hoạt động
ESP32 kết nối vào mạng WiFi trong nhà, được router cấp một địa chỉ IP và mở web server ở cổng 80. Điện thoại trong cùng mạng mở địa chỉ đó sẽ nhận được trang có trạng thái từng relay và hai nút BẬT, TẮT. Mỗi lần bấm nút, trình duyệt gửi yêu cầu tới ESP32 để đổi mức chân IN của module relay.
2. Module relay và mức kích
| Thông số | Giá trị |
|---|---|
| Số kênh | 1, 2 hoặc 4 kênh (bài này dùng loại 4 kênh) |
| Cuộn relay | 5 V hoặc 12 V tùy phiên bản, in trên thân relay (05VDC, 12VDC) |
| Tải của tiếp điểm | Tối đa AC 250 V/10 A hoặc DC 30 V/10 A |
| Cách ly, hiển thị | Optocoupler; LED báo cho từng kênh |
| Chân điều khiển (loại 4 kênh) | GND, IN1–IN4, VCC; jumper JD-VCC – VCC |
| Ngõ ra mỗi kênh | 3 cọc vít COM, NO, NC |
Mức kích là mức logic ở chân IN làm relay đóng. Module 4 kênh trong bài kích ở mức thấp (active LOW): IN ở mức LOW thì relay đóng, LED của kênh sáng; IN ở mức HIGH thì relay nhả. Loại 1 kênh có jumper chọn kích mức cao hoặc mức thấp. Chương trình khai báo mức kích bằng một hằng số RELAY_ACTIVE_LOW; với module kích mức cao chỉ cần đổi thành false. Bản 12 V cần thêm nguồn 12 V riêng cho cuộn relay; bài này đấu nối với bản 5 V.
3. Linh kiện cần chuẩn bị
- ESP32 DevKit V1 30 chân (bản dùng chip CH340 làm tương tự) và cáp USB có truyền dữ liệu.
- Module relay 4 kênh, bản 5 V.
- 6 dây jumper cái – cái; có thể thêm đế ra chân ESP32 30 chân để nối dây gọn hơn.
- Mạng WiFi 2,4 GHz (ESP32 không dùng băng tần 5 GHz) và điện thoại kết nối cùng mạng.
- Máy tính đã cài Arduino IDE 2.x.
- Nguồn 5 V từ 1 A trở lên nếu bật nhiều relay cùng lúc.
4. Sơ đồ nối dây
| Module relay | ESP32 DevKit V1 | Ghi chú |
|---|---|---|
| GND | GND | Mass chung. |
| VCC | VIN | 5 V từ cổng USB; giữ jumper JD-VCC – VCC. |
| IN1 | D18 / GPIO18 | Kênh 1. |
| IN2 | D19 / GPIO19 | Kênh 2. |
| IN3 | D21 / GPIO21 | Kênh 3. |
| IN4 | D22 / GPIO22 | Kênh 4. |
- Chọn chân: bốn chân GPIO giống bài relay cơ bản và không phải chân strapping. GPIO21, GPIO22 là chân I2C mặc định; nếu cần dùng I2C, chuyển IN3, IN4 sang chân khác rồi sửa mảng
RELAY_PINS. - Mức logic: ESP32 xuất mức 3,3 V, module cấp 5 V. Module kích mức thấp đóng relay khi IN bị kéo xuống 0 V nên GPIO điều khiển trực tiếp được. Không đưa 5 V vào chân 3V3 hay chân GPIO.
- Nguồn: mỗi cuộn relay 5 V tiêu thụ khoảng 70 mA. Thử một, hai kênh có thể lấy 5 V từ chân VIN; khi bật cả bốn kênh, cấp nguồn 5 V ngoài cho VCC và GND của module, nối chung GND với ESP32.
Chưa nối tải ở bước này: tiếng tách của relay và LED của kênh đủ để kiểm tra. Lưu ý về jumper JD-VCC, cách kiểm tra các cọc COM, NO, NC và an toàn khi đấu tải xem bài relay cơ bản.
5. Cài đặt Arduino IDE
- Mở Tools > Board > Boards Manager, tìm esp32 và cài gói esp32 by Espressif Systems.
- Chọn Tools > Board > esp32 > DOIT ESP32 DEVKIT V1 (hoặc ESP32 Dev Module) và chọn đúng cổng COM.
- Không cần cài thêm thư viện:
WiFivàWebServercó sẵn trong gói esp32.
Bài dùng gói esp32 phiên bản 3.3.10.
6. Chương trình web server điều khiển relay
Tạo sketch mới, dán chương trình dưới đây, sửa WIFI_SSID và WIFI_PASSWORD thành tên và mật khẩu WiFi nhà bạn rồi nạp vào ESP32:
/*
PYWORLD.VN - ESP32 DevKit V1 làm web server điều khiển module relay 4 kênh qua WiFi
Nối dây: IN1 = GPIO18, IN2 = GPIO19, IN3 = GPIO21, IN4 = GPIO22
Thư viện: WiFi, WebServer (có sẵn trong gói esp32 by Espressif Systems)
*/
#include <WiFi.h>
#include <WebServer.h>
// ====== WiFi: đổi thành tên và mật khẩu WiFi nhà bạn ======
const char* WIFI_SSID = "TEN_WIFI";
const char* WIFI_PASSWORD = "MAT_KHAU_WIFI";
const uint32_t WIFI_TIMEOUT_MS = 20000; // Chờ kết nối tối đa 20 giây
// ====== Relay ======
const uint8_t RELAY_PINS[] = {18, 19, 21, 22}; // IN1, IN2, IN3, IN4
const uint8_t RELAY_COUNT = sizeof(RELAY_PINS) / sizeof(RELAY_PINS[0]);
// true: module kích mức thấp (IN = LOW thì relay đóng)
// false: module kích mức cao (IN = HIGH thì relay đóng)
const bool RELAY_ACTIVE_LOW = true;
bool relayOn[RELAY_COUNT]; // Trạng thái từng kênh, true = relay đang đóng
WebServer server(80); // Web server ở cổng HTTP 80
// ====== Trang web: phần đầu, khung của một kênh, phần cuối ======
const char PAGE_HEAD[] = R"rawliteral(<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ESP32 Relay</title>
<style>
body { margin: 0; font-family: Arial, sans-serif; background: #eef2f7; color: #1d2733; }
main { max-width: 420px; margin: 0 auto; padding: 16px; }
h1 { font-size: 22px; margin: 8px 0 16px; }
form { background: #fff; border-radius: 12px; padding: 14px; margin-bottom: 12px; }
p { display: flex; justify-content: space-between; margin: 0 0 12px; font-size: 18px; }
.on { color: #2b8a3e; }
.off { color: #868e96; }
div { display: flex; }
button { flex: 1; padding: 14px 0; border: 0; border-radius: 10px; font-size: 18px; font-weight: bold; color: #fff; }
.bat { background: #2f9e44; margin-right: 10px; }
.tat { background: #e03131; }
small { display: block; text-align: center; color: #5b6675; }
</style>
</head>
<body>
<main>
<h1>Điều khiển relay qua WiFi</h1>
)rawliteral";
const char PAGE_ROW[] = R"rawliteral(<form method="post" action="/relay">
<input type="hidden" name="ch" value="%CH%">
<p><span>Relay %CH%</span><b class="%CLASS%">%TEXT%</b></p>
<div>
<button class="bat" name="state" value="1">BẬT</button>
<button class="tat" name="state" value="0">TẮT</button>
</div>
</form>
)rawliteral";
const char PAGE_FOOT[] = R"rawliteral(<small>ESP32 - %IP%</small>
</main>
</body>
</html>
)rawliteral";
// Đóng (on = true) hoặc nhả (on = false) một relay, index tính từ 0
void setRelay(uint8_t index, bool on) {
if (index >= RELAY_COUNT) return;
bool level = RELAY_ACTIVE_LOW ? !on : on;
digitalWrite(RELAY_PINS[index], level ? HIGH : LOW);
relayOn[index] = on;
}
// Trang chính: mỗi kênh có trạng thái hiện tại và hai nút BẬT, TẮT
void handleRoot() {
String page = PAGE_HEAD;
for (uint8_t i = 0; i < RELAY_COUNT; i++) {
String row = PAGE_ROW;
row.replace("%CH%", String(i + 1));
row.replace("%CLASS%", relayOn[i] ? "on" : "off");
row.replace("%TEXT%", relayOn[i] ? "ĐANG BẬT" : "ĐANG TẮT");
page += row;
}
page += PAGE_FOOT;
page.replace("%IP%", WiFi.localIP().toString());
server.sendHeader("Cache-Control", "no-store");
server.send(200, "text/html; charset=utf-8", page);
}
// Nhận lệnh từ nút bấm: ch = số kênh (1..4), state = 1 (bật) hoặc 0 (tắt)
void handleRelay() {
int ch = server.arg("ch").toInt();
String state = server.arg("state");
if (ch < 1 || ch > RELAY_COUNT || (state != "1" && state != "0")) {
server.send(400, "text/plain", "Yeu cau khong hop le");
return;
}
setRelay(ch - 1, state == "1");
Serial.printf("Relay %d: %s\n", ch, relayOn[ch - 1] ? "BAT" : "TAT");
// Chuyển hướng về trang chính để trình duyệt hiển thị trạng thái mới
server.sendHeader("Location", "/");
server.send(303);
}
void handleNotFound() {
server.send(404, "text/plain", "Khong tim thay trang");
}
void connectWiFi() {
Serial.print("Dang ket noi WiFi: ");
Serial.println(WIFI_SSID);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
uint32_t start = millis();
while (WiFi.status() != WL_CONNECTED) {
if (millis() - start > WIFI_TIMEOUT_MS) {
Serial.println();
Serial.println("Khong ket noi duoc WiFi. Kiem tra ten va mat khau WiFi.");
Serial.println("Khoi dong lai sau 5 giay...");
delay(5000);
ESP.restart();
}
delay(500);
Serial.print(".");
}
Serial.println();
Serial.println("Da ket noi WiFi.");
Serial.print("Mo trinh duyet tai dia chi: http://");
Serial.println(WiFi.localIP());
}
void setup() {
// Giữ relay tắt khi khởi động: ghi sẵn mức tắt rồi mới bật ngõ ra
for (uint8_t i = 0; i < RELAY_COUNT; i++) {
pinMode(RELAY_PINS[i], INPUT); // Chân chưa xuất tín hiệu, relay chưa bị kích
setRelay(i, false); // Ghi sẵn mức tắt
pinMode(RELAY_PINS[i], OUTPUT); // Bật ngõ ra, chân xuất ngay mức tắt
}
Serial.begin(115200);
delay(200);
connectWiFi();
server.on("/", HTTP_GET, handleRoot);
server.on("/relay", HTTP_POST, handleRelay);
server.onNotFound(handleNotFound);
server.begin();
Serial.println("Web server da san sang.");
}
void loop() {
server.handleClient(); // Xử lý yêu cầu từ trình duyệt
}
7. Tìm địa chỉ IP và mở trang điều khiển
- Mở Tools > Serial Monitor, chọn 115200 baud rồi nhấn nút EN trên ESP32.
- Chờ ESP32 kết nối WiFi. Serial Monitor hiển thị tương tự:
Dang ket noi WiFi: TEN_WIFI
......
Da ket noi WiFi.
Mo trinh duyet tai dia chi: http://192.168.1.50
Web server da san sang.
- Kết nối điện thoại vào cùng mạng WiFi, mở trình duyệt và gõ đúng địa chỉ vừa đọc được (
192.168.1.50chỉ là ví dụ). - Bấm BẬT hoặc TẮT ở từng kênh: relay kêu tách, LED của kênh và dòng trạng thái trên trang đổi theo; Serial Monitor in thêm dòng như
Relay 1: BAT.
Địa chỉ IP do router cấp nên có thể thay đổi. Muốn cố định, gán IP theo địa chỉ MAC trong phần DHCP của router hoặc đặt IP tĩnh bằng WiFi.config() trước WiFi.begin().
8. Giải thích các lệnh chính
setRelay(index, on): đổi yêu cầu bật, tắt thành mức HIGH hoặc LOW theoRELAY_ACTIVE_LOWvà lưu trạng thái vàorelayOn[].WiFi.begin(WIFI_SSID, WIFI_PASSWORD): kết nối vào router.WiFi.status()bằngWL_CONNECTEDkhi đã có IP,WiFi.localIP()trả về địa chỉ đó. Quá 20 giây chưa kết nối được, chương trình báo lỗi rồi khởi động lại.server.on("/", HTTP_GET, handleRoot): trang chính được ghép từ ba khốiPAGE_HEAD,PAGE_ROWvàPAGE_FOOT.PAGE_ROWdùng một lần cho mỗi kênh, các chỗ%CH%,%CLASS%,%TEXT%được thay bằng số kênh và trạng thái hiện tại.server.on("/relay", HTTP_POST, handleRelay): mỗi nút gửi form bằng phương thức POST với tham sốch(số kênh) vàstate(1 là bật, 0 là tắt), đọc bằngserver.arg(). Vì chỉ nhận POST nên mở lại địa chỉ hay tải lại trang không làm relay đổi trạng thái. Xử lý xong,server.send(303)kèm tiêu đềLocationđưa trình duyệt về trang chính với trạng thái mới.server.handleClient(): phải được gọi liên tục trongloop(); không thêmdelay()dài.
Trang chỉ gồm HTML và CSS nhúng sẵn trong chương trình, không tải gì từ Internet.
9. Giữ relay tắt khi khởi động
Relay phải nhả từ lúc cấp nguồn cho tới khi có người bấm nút. Chương trình làm ba việc:
- Dùng chân không phải strapping (tránh GPIO0, GPIO2, GPIO5, GPIO12, GPIO15) và không dùng TX0, RX0.
- Cấu hình chân relay ở đầu
setup(), trước khi kết nối WiFi. - Ghi mức tắt trước khi bật ngõ ra. Với ESP32 Arduino core 3.x,
digitalWrite()chỉ có tác dụng sau khi chân đã quapinMode(), nên chương trình khai báoINPUT, ghi sẵn mức tắt rồi mới chuyển sangOUTPUT.
Sau khi khởi động lại, cả bốn kênh đều TẮT; chương trình không lưu trạng thái cũ. Trong lúc ESP32 reset, trạng thái chân do phần cứng quyết định; nếu tải không được phép đóng nhầm, xem lưu ý về điện trở kéo trong bài relay cơ bản.
10. Giới hạn bảo mật và an toàn điện
- Chỉ dùng trong mạng nội bộ: trang không có mật khẩu và không mã hóa; ai vào được WiFi và biết địa chỉ IP đều bật, tắt được relay. Không mở cổng trên router (port forwarding) để điều khiển từ Internet.
- Khi mất WiFi: relay giữ nguyên trạng thái cuối cho tới khi ESP32 kết nối lại. Không dùng ví dụ này cho tải có thể gây nguy hiểm khi bật ngoài ý muốn hoặc không tắt kịp như thiết bị gia nhiệt, máy bơm, cửa cuốn.
- Một kết nối mỗi lúc: thư viện WebServer xử lý lần lượt từng yêu cầu, phù hợp để học và dùng trong gia đình.
Cảnh báo điện lưới: hãy chạy thử với tải điện áp thấp trước (ví dụ đèn LED 12 V dùng adapter) hoặc chỉ quan sát LED trên module. Điện 220 V có thể gây điện giật chết người và cháy nổ. Chỉ đấu tải 220 V khi mạch nằm trong hộp cách điện kín, dây và mối nối được bọc cách điện, có cầu chì hoặc aptomat phù hợp, và do người có chuyên môn về điện thực hiện lúc đã ngắt nguồn. Mặt dưới module có mối hàn hở nối với tiếp điểm, không chạm vào mạch khi tải đang có điện. Mức 250 V/10 A là giới hạn tối đa; tải có dòng khởi động lớn như động cơ, máy bơm phải chọn thấp hơn nhiều.
11. Xử lý lỗi thường gặp
- Serial Monitor báo "Khong ket noi duoc WiFi": kiểm tra tên, mật khẩu WiFi (phân biệt chữ hoa, chữ thường); router phải phát băng tần 2,4 GHz; đặt ESP32 gần router hơn.
- Có địa chỉ IP nhưng điện thoại không mở được trang: điện thoại phải cùng mạng WiFi (tắt dữ liệu di động khi thử), gõ
http://chứ không phảihttps://. Mạng khách hoặc chế độ cách ly thiết bị (AP isolation) của router cũng chặn kết nối. - Bấm BẬT thì relay nhả, bấm TẮT thì relay đóng: module kích mức cao; đổi
RELAY_ACTIVE_LOWthànhfalse. - LED của kênh sáng nhưng relay không đóng: cuộn relay thiếu nguồn; kiểm tra jumper JD-VCC – VCC và dây VCC phải nối VIN, không phải 3V3.
- ESP32 tự khởi động lại khi relay đóng: nguồn sụt áp (Serial Monitor có thể hiện Brownout detector was triggered); cấp nguồn 5 V ngoài cho module như mục 4.
- Trang hiện trạng thái cũ: trang chỉ cập nhật lúc được tải; hãy tải lại trang khi có nhiều điện thoại cùng điều khiển.
- Không nạp được chương trình: dùng cáp USB có truyền dữ liệu, chọn đúng cổng COM, cài driver USB-UART nếu máy không nhận cổng; giữ nút BOOT khi IDE dừng ở Connecting....
Xem thêm: ESP32 điều khiển module relay 4 kênh an toàn, nạp file .bin vào ESP32 bằng Flash Download Tool và các mạch ESP32 tại PYWORLD.
Xem thêm