ESP32 WebSocket Server Hướng Dẫn Giao Tiếp Thời Gian Thực

WebServerWithWebSocket Example - Giao Tiếp Thời Gian Thực

Tổng Quan

Ví dụ này minh họa cách tạo một web server đa trang nâng cao với giao tiếp WebSocket thời gian thực trên ESP32, cho phép trao đổi dữ liệu hai chiều giữa trình duyệt và ESP32.

Tính Năng

  • Giao tiếp WebSocket thời gian thực cho nhắn tin hai chiều tức thì
  • Lệnh dựa trên chuỗi đơn giản (ping, hello, time, led on/off)
  • Điều khiển LED qua lệnh WebSocket
  • HTTP server tích hợp phục vụ giao diện test WebSocket
  • Chức năng echo để kiểm tra truyền tin
  • Giám sát trạng thái kết nối với tự động kết nối lại

Linh Kiện Cần Thiết

1×mô-đun phát triển ESP-WROOM-32
1×Alternatively, ESP32 Uno-form board
1×Alternatively, ESP32 S3 Uno-form board
1×Cáp USB Type-C
1×(Khuyến nghị) Screw Terminal Expansion Board for ESP32
1×(Khuyến nghị) Breakout Expansion Board for ESP32
1×(Khuyến nghị) Power Splitter for ESP32

Or you can buy the following kits:

1×DIYables ESP32 Starter Kit (ESP32 included)
1×DIYables Sensor Kit (30 sensors/displays)
1×DIYables Sensor Kit (18 sensors/displays)

Cài Đặt Thư Viện

Thực hiện theo các bước sau:

  • Nếu đây là lần đầu bạn sử dụng ESP32, hãy tham khảo hướng dẫn về thiết lập môi trường cho ESP32 trong Arduino IDE.
  • Kết nối board ESP32 với máy tính bằng cáp USB.
  • Mở Arduino IDE trên máy tính.
  • Chọn board ESP32 phù hợp (ví dụ: ESP32) và cổng COM.
  • Mở Library Manager bằng cách nhấp vào biểu tượng Library Manager ở phía bên trái của Arduino IDE.
  • Tìm kiếm Web Server for ESP32 và tìm mWebSockets by DIYables.
  • Nhấp vào nút Install để thêm thư viện mWebSockets.
ESP32 web server thư viện

Ví Dụ WebSocket

  • Trong Arduino IDE, đi đến File Examples Web Server for ESP32 WebServerWithWebSocket để mở mã ví dụ

Cấu Trúc Code

Dự án bao gồm hai file chính:

  1. websocket_html.h:

Chứa mã HTML, CSS và JavaScript cho giao diện web. File này định nghĩa giao diện người dùng để tương tác với ESP32 qua WebSocket, bao gồm các nút gửi lệnh, nhật ký tin nhắn và chỉ báo trạng thái kết nối.

  1. WebServerWithWebSocket.ino:

Triển khai logic server chính trên ESP32. File này thiết lập HTTP và WebSocket server, quản lý kết nối WiFi, xử lý tin nhắn WebSocket đến và điều khiển phần cứng (như LED tích hợp) dựa trên lệnh nhận được.

Kết Nối Mạch

Không cần linh kiện ngoài - ví dụ này sử dụng LED tích hợp ở pin 13.

Giao Tiếp WebSocket

Chi Tiết Kết Nối

  • Cổng Web Server: 80 (HTTP)
  • Cổng WebSocket: 81 (WebSocket)
  • Giao thức: WebSocket tuân thủ RFC 6455

Loại Tin Nhắn

Triển khai thực tế sử dụng lệnh chuỗi đơn giản:

  • Ping: "ping" → Phản hồi: "pong"
  • Chào hỏi: "hello" → Phản hồi: "Hello from ESP32!"
  • Thời gian hoạt động: "time" → Phản hồi: "Uptime: X seconds"
  • Điều khiển LED: "led on" / "led off" → Phản hồi: "LED ON" / "LED OFF"
  • Echo: Bất kỳ văn bản nào khác → Phản hồi: "Echo: [tin nhắn của bạn]"

Hướng Dẫn Thiết Lập

1. Cấu Hình Mạng

Chỉnh sửa thông tin WiFi trực tiếp trong file WebServerWithWebSocket.ino:

const char WIFI_SSID[] = "YOUR_WIFI_SSID"; const char WIFI_PASSWORD[] = "YOUR_WIFI_PASSWORD";

2. Upload Code và Giám Sát Output

  • Kết nối ESP32 với máy tính
  • Chọn đúng board và port trong Arduino IDE
  • Upload sketch WebServerWithWebSocket.ino
  • Mở Serial Monitor (9600 baud)
  • Chờ kết nối WiFi
  • Ghi chú cả địa chỉ HTTP và WebSocket server
  • Nếu bạn không thấy địa chỉ IP trong Serial monitor, hãy nhấn nút reset trên board ESP32
  • Mở trình duyệt web và nhập địa chỉ IP của ESP32 vào thanh địa chỉ (ví dụ: http://192.168.x.x/). Bạn sẽ thấy giao diện web như bên dưới:
ESP32 websocket interface
  • Nhấp nút "Connect" để kết nối web với ESP32 qua WebSocket.
  • Khi trạng thái kết nối hiển thị "Connected", hãy nhấp từng nút một để kiểm tra giao tiếp hai chiều giữa giao diện web và ESP32 qua WebSocket. Kết quả sẽ như bên dưới:
ESP32 websocket demo

Tính Năng Giao Diện Web

Giao diện HTML (từ websocket_html.h) cung cấp:

  • Quản lý kết nối WebSocket với nút connect/disconnect
  • Nút lệnh đơn giản cho ping, hello, time, LED on/off
  • Trường nhập tin nhắn để gửi lệnh tùy chỉnh
  • Lịch sử tin nhắn thời gian thực hiển thị tin nhắn đã gửi và nhận
  • Chỉ báo trạng thái kết nối hiển thị trạng thái WebSocket

Giải Thích Code

Thiết Lập WebSocket Server

#include <DIYables_ESP32_WebServer.h> #include "websocket_html.h" // LED configuration #define LED_PIN 2 // ESP32 built-in LED pin // Server configuration WiFiServer httpServer(80); constexpr uint16_t wsPort = 81; WebSocketServer wss{wsPort}; void setup() { Serial.begin(9600); delay(1000); // Initialize built-in LED pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, LOW); // Connect to WiFi manually Serial.print("Connecting to "); Serial.println(WIFI_SSID); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } // Start HTTP server httpServer.begin(); // Configure WebSocket server with lambda handlers wss.onConnection([](WebSocket &ws) { Serial.println("New WebSocket connection"); // Set up message handler ws.onMessage([](WebSocket &ws, const WebSocket::DataType dataType, const char *message, uint16_t length) { handleWebSocketMessage(ws, message, length); }); // Send welcome message const char welcome[] = "Connected to ESP32!"; ws.send(WebSocket::DataType::TEXT, welcome, strlen(welcome)); }); // Start WebSocket server wss.begin(); }

Xử Lý Tin Nhắn WebSocket

void handleWebSocketMessage(WebSocket &ws, const char *message, uint16_t length) { Serial.print("[WebSocket] Received ("); Serial.print(length); Serial.print(" bytes): "); Serial.println(message); String msgStr = String(message); String response = ""; // Command processing with simple string matching if (msgStr.equalsIgnoreCase("ping")) { response = "pong"; } else if (msgStr.equalsIgnoreCase("hello")) { response = "Hello from ESP32!"; } else if (msgStr.equalsIgnoreCase("time")) { response = "Uptime: " + String(millis()/1000) + " seconds"; } else if (msgStr.equalsIgnoreCase("led on")) { digitalWrite(LED_PIN, HIGH); response = "LED ON"; } else if (msgStr.equalsIgnoreCase("led off")) { digitalWrite(LED_PIN, LOW); response = "LED OFF"; } else { response = "Echo: " + msgStr; } // Send response ws.send(WebSocket::DataType::TEXT, response.c_str(), response.length()); }

Vòng Lặp Chính với Xử Lý WebSocket

void loop() { // Handle HTTP requests WiFiClient httpClient = httpServer.available(); if (httpClient) { handleHTTPClient(httpClient); } // Handle WebSocket connections wss.listen(); delay(10); } // HTTP request handler void handleHTTPClient(WiFiClient client) { String request = ""; // Read HTTP request while (client.connected() && client.available()) { String line = client.readStringUntil('\n'); if (line == "\r") break; if (request.length() == 0) request = line; } // Serve web page or 404 if (request.indexOf("GET / HTTP") >= 0) { client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println("Connection: close"); client.println(); client.print(htmlPage); } else { client.println("HTTP/1.1 404 Not Found"); client.println("Connection: close"); client.println(); } client.stop(); }

JavaScript WebSocket Client

Quản Lý Kết Nối

let websocket; let isConnected = false; function initWebSocket() { const wsUrl = `ws://${window.location.hostname}:81/`; websocket = new WebSocket(wsUrl); websocket.onopen = function(event) { console.log('WebSocket connected'); isConnected = true; updateConnectionStatus('Connected'); }; websocket.onmessage = function(event) { handleWebSocketMessage(JSON.parse(event.data)); }; websocket.onclose = function(event) { console.log('WebSocket disconnected'); isConnected = false; updateConnectionStatus('Disconnected'); // Auto-reconnect after 3 seconds setTimeout(initWebSocket, 3000); }; websocket.onerror = function(error) { console.error('WebSocket error:', error); updateConnectionStatus('Error'); }; }

Xử Lý Tin Nhắn

function handleWebSocketMessage(event) { const message = event.data; console.log('Received:', message); // Handle simple string responses (matching actual implementation) if (message === 'pong') { console.log('Ping response received'); addMessageToHistory('Received: pong'); } else if (message.startsWith('Hello from ESP32')) { addMessageToHistory('Received: ' + message); } else if (message.startsWith('Uptime:')) { updateUptimeDisplay(message); addMessageToHistory('Received: ' + message); } else if (message === 'LED ON' || message === 'LED OFF') { updateLedStatus(message); addMessageToHistory('Received: ' + message); } else if (message.startsWith('Echo:')) { addMessageToHistory('Received: ' + message); } else { addMessageToHistory('Received: ' + message); } }

Gửi Lệnh

// Simple string-based command sending (matching actual implementation) function sendCommand(command) { if (isConnected) { websocket.send(command); addMessageToHistory('Sent: ' + command); } else { alert('WebSocket not connected!'); } } function controlLED(action) { if (action === 'on') { sendCommand('led on'); } else if (action === 'off') { sendCommand('led off'); } } function sendPing() { sendCommand('ping'); } function sendHello() { sendCommand('hello'); } function getUptime() { sendCommand('time'); }

Tính Năng Giao Diện HTML

Giao Diện Test WebSocket

Trang HTML bao gồm cung cấp giao diện test WebSocket hoàn chỉnh:

  • Điều khiển kết nối với chức năng connect/disconnect
  • Nút lệnh nhanh cho các lệnh thông dụng (ping, hello, time, điều khiển LED)
  • Trường nhập tin nhắn để gửi lệnh văn bản tùy chỉnh
  • Nhật ký tin nhắn thời gian thực hiển thị toàn bộ giao tiếp
  • Hiển thị trạng thái kết nối với chỉ báo trực quan

Hàm JavaScript Tích Hợp

Giao diện HTML bao gồm các hàm JavaScript phù hợp với cấu trúc lệnh ESP32:

// Send simple string commands to ESP32 function sendQuick(msg) { if (connected && ws) { ws.send(msg); addMsg('You: ' + msg, 'sent'); } else { addMsg('Not connected!'); } } // Handle WebSocket events ws.onmessage = function(event) { console.log('WebSocket message received:', event.data); addMsg('ESP32: ' + event.data, 'received'); };

Ghi Chú Triển Khai

Kiến Trúc Đơn Giản

Triển khai thực tế sử dụng phương pháp cơ bản:

  • Server riêng biệt: WiFiServer cho HTTP và WebSocketServer cho WebSocket
  • Kết nối WiFi thủ công: Thiết lập WiFi.begin() tiêu chuẩn không có quản lý server tích hợp
  • Nhắn tin dựa trên chuỗi: Lệnh chuỗi đơn giản thay vì giao thức JSON
  • Lambda handlers: Trình xử lý sự kiện WebSocket được định nghĩa như hàm lambda inline
  • Phục vụ HTML cơ bản: client.print() trực tiếp nội dung HTML từ header file

Hạn Chế của Triển Khai Hiện Tại

  • Không có streaming dữ liệu cảm biến (như hiển thị trong ví dụ tài liệu)
  • Không có phân tích tin nhắn JSON
  • Không có broadcasting đến nhiều client
  • Không có quản lý kết nối ngoài connect/disconnect cơ bản
  • Không có hệ thống heartbeat hoặc keepalive
  • Không có tính năng hàng đợi tin nhắn hoặc độ tin cậy

Lệnh Có Sẵn

Các lệnh hoạt động trong triển khai thực tế:

  • pingpong
  • helloHello from ESP32!
  • timeUptime: X seconds
  • led onLED ON (bật LED_PIN)
  • led offLED OFF (tắt LED_PIN)
  • Bất kỳ văn bản nào khác → Echo: [tin nhắn của bạn]

Khắc Phục Sự Cố

Vấn Đề Thường Gặp

Kết Nối WebSocket Thất Bại

  • Kiểm tra xem cổng WebSocket (81) có thể truy cập được không
  • Xác minh cài đặt firewall không chặn cổng 81
  • Sử dụng developer tools của trình duyệt để kiểm tra lỗi WebSocket
  • Đảm bảo địa chỉ IP ESP32 chính xác

Tin Nhắn Không Nhận Được

  • Kiểm tra Serial Monitor để xem sự kiện WebSocket và việc nhận tin nhắn
  • Xác minh chuỗi lệnh khớp chính xác (không phân biệt chữ hoa thường)
  • Kiểm tra với lệnh đơn giản như "ping" trước

LED Không Phản Hồi

  • Xác nhận LED_PIN được định nghĩa đúng cho board ESP32 của bạn (thường là GPIO 2)
  • Kiểm tra Serial Monitor để xem tin nhắn xử lý lệnh
  • Xác minh lệnh "led on" và "led off" được gửi chính xác

Lệnh Debug

Sử dụng các lệnh này để kiểm tra chức năng WebSocket:

  • ping - Kiểm tra kết nối đơn giản
  • hello - Kiểm tra phản hồi chào hỏi
  • time - Kiểm tra thời gian hoạt động ESP32
  • led on / led off - Kiểm tra điều khiển phần cứng
  • Bất kỳ văn bản nào khác sẽ echo lại để kiểm tra giao tiếp

Ứng Dụng Thực Tế

Điều Khiển IoT Cơ Bản

Triển khai đơn giản hiện tại phù hợp cho:

  • Điều khiển LED từ xa để chỉ báo trạng thái thiết bị cơ bản
  • Hệ thống lệnh/phản hồi đơn giản để tương tác thiết bị
  • Kiểm tra kết nối WebSocket cho mục đích phát triển
  • Minh họa giao tiếp thời gian thực cơ bản

Cải Tiến Tiềm Năng

Để mở rộng ví dụ này cho sử dụng thương mại, hãy xem xét thêm:

  • Phân tích tin nhắn JSON sử dụng thư viện ArduinoJson
  • Khả năng streaming dữ liệu cảm biến
  • Quản lý kết nối nhiều WebSocket client
  • Tính năng xác thực và bảo mật
  • Logic xử lý lỗi và kết nối lại

Bước Tiếp Theo

  • Thêm phân tích tin nhắn JSON sử dụng thư viện ArduinoJson
  • Tích hợp cảm biến thực tế (nhiệt độ, độ ẩm, v.v.)
  • Triển khai broadcasting đến nhiều WebSocket client
  • Thêm xác thực cho truy cập an toàn
  • Tạo giao diện ứng dụng di động sử dụng kết nối WebSocket