Trong bài này chúng ta sẽ tạm gác HTTP sang một bên, lập trình ESP32 TCP Socket: làm việc trực tiếp với TCP Socket thô — lớp giao thức nằm ngay bên dưới HTTP. Hiểu được TCP hoạt động thế nào giúp bạn có cái nhìn rõ ràng hơn về mọi giao thức mạng đã học trước đó, vì HTTP, MQTT, WebSocket… tất cả đều chạy trên nền TCP.
Đây là một bài thực hành trong Serie Lập trình ESP32 từ A tới Z, đào sâu hơn vào tầng mạng so với các bài Webserver trước.
TCP khác HTTP ở chỗ nào?
Thực ra HTTP không phải là 1 giao thức mạng độc lập — nó chỉ là một quy ước về cách format dữ liệu (dòng đầu GET / HTTP/1.1, các header, dòng trống, rồi tới body) được truyền đi bên trên một kết nối TCP đã có sẵn. Ở Bài 1, khi mình đọc từng byte từ webClient và tìm chuỗi "GET /led1/on", thực chất đó đã là code TCP rồi — chỉ là mình đang tự tay parse cú pháp HTTP bên trên nó.
Bài này chúng ta bỏ hẳn lớp cú pháp HTTP, chỉ gửi/nhận text thuần túy tùy ý qua TCP — không có GET, không có header, không có gì cả ngoài dữ liệu bạn muốn gửi.
Lập trình ESP32 TCP Socket làm server
Mở cổng TCP tùy chọn
const uint16_t tcpPort = 8888; // khac cong 80 mac dinh cua HTTP
WiFiServer tcpServer(tcpPort);
void setup() {
...
tcpServer.begin();
}
Với HTTP, mọi trình duyệt đều ngầm hiểu phải kết nối tới cổng 80 (hoặc 443 cho HTTPS). Với TCP thô, bạn tự chọn số cổng bất kỳ (miễn không trùng cổng hệ thống đã dùng) — ở đây mình chọn 8888.
Đọc và xử lý dữ liệu client gửi tới
while (client.connected()) {
if (client.available()) {
String line = client.readStringUntil('\n');
line.trim();
if (line == "LED_ON") {
digitalWrite(led, HIGH);
client.println("Da bat LED");
} else if (line == "LED_OFF") {
digitalWrite(led, LOW);
client.println("Da tat LED");
} else if (line.length() > 0) {
client.println("Echo: " + line);
}
}
}
readStringUntil('\n') đọc dữ liệu tới khi gặp ký tự xuống dòng — đây là quy ước do chính mình đặt ra (giao thức “tự chế”), khác hẳn cú pháp cố định của HTTP. Gửi đúng chuỗi LED_ON hoặc LED_OFF thì ESP32 điều khiển LED, còn lại thì echo nguyên văn lại cho client — kiểu server TCP echo kinh điển.
Lưu ý: TCP đảm bảo dữ liệu đến đúng thứ tự và không bị mất (nếu mất, giao thức tự động gửi lại) — đây là lý do TCP phù hợp cho các lệnh điều khiển quan trọng như bật/tắt LED. Đánh đổi là TCP cần thiết lập kết nối trước khi truyền dữ liệu (bắt tay 3 bước) nên có độ trễ khởi tạo, không phù hợp cho dữ liệu cần gửi cực nhanh, số lượng lớn, chấp nhận thi thoảng mất gói — trường hợp đó nên dùng UDP.
Code đầy đủ
#include <Arduino.h>
#include <WiFi.h>
const int led = 26;
const char* ssid = "Ten_Wifi_Cua_Ban";
const char* password = "Mat_Khau_Wifi";
const uint16_t tcpPort = 8888;
WiFiServer tcpServer(tcpPort);
void setup() {
Serial.begin(115200);
pinMode(led, OUTPUT);
digitalWrite(led, LOW);
Serial.print("Connecting to wifi ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected.");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
Serial.print("TCP server dang lang nghe tai cong: ");
Serial.println(tcpPort);
tcpServer.begin();
}
void loop() {
WiFiClient client = tcpServer.available();
if (client) {
Serial.println("Co client TCP moi ket noi toi");
client.println("Xin chao tu ESP32! Go LED_ON hoac LED_OFF roi Enter:");
while (client.connected()) {
if (client.available()) {
String line = client.readStringUntil('\n');
line.trim();
Serial.print("Nhan tu client: ");
Serial.println(line);
if (line == "LED_ON") {
digitalWrite(led, HIGH);
client.println("Da bat LED");
} else if (line == "LED_OFF") {
digitalWrite(led, LOW);
client.println("Da tat LED");
} else if (line.length() > 0) {
client.println("Echo: " + line);
}
}
}
client.stop();
Serial.println("Client TCP ngat ket noi");
}
}
Code đầy đủ mình để trên GitHub, các bạn xem tại 04-Protocol/05_TCP_Socket nhé.
Kết quả
Sơ đồ đấu nối: LED nối GPIO26 qua điện trở 220Ω, chân âm LED xuống GND.
Nạp code, lấy IP của ESP32 từ Serial Monitor. Trên máy tính (Windows/Linux/Mac đều có sẵn công cụ), mở terminal gõ:
telnet <IP_ESP32> 8888
hoặc nếu máy có netcat:
nc <IP_ESP32> 8888
Phía máy tính, kết nối bằng nc rồi gõ lần lượt LED_ON, LED_OFF và 1 chuỗi bất kỳ:
Log Serial Monitor của ESP32 cùng lúc đó:
Gõ LED_ON rồi Enter, LED trên board phải sáng ngay lập tức và terminal nhận được phản hồi “Da bat LED”. Gõ chữ bất kỳ khác, ESP32 sẽ echo nguyên văn lại, đúng không nào.
Kết
Hiểu TCP Socket ở tầng thấp giúp bạn tự tin hơn khi đọc code của các thư viện giao thức cao hơn (HTTP, MQTT, WebSocket) — vì bản chất chúng đều chỉ là những quy ước format khác nhau chạy trên cùng nền TCP này. Ở bài tiếp theo, chúng ta sẽ tìm hiểu UDP — người anh em của TCP nhưng theo triết lý hoàn toàn ngược lại: nhanh nhưng không đảm bảo.
Nếu thấy bài viết có ích, hãy chia sẻ cho bạn bè cùng học. Có thắc mắc gì cứ để lại bình luận, mình sẽ giải đáp nhé!





