Cách tạo ESP32 Web Server với Arduino IDE
Trong bài viết này, chúng ta sẽ tạo một Web Server ESP32 độc lập để điều khiển các thiết bị đầu ra (cụ thể là hai đèn LED) sử dụng môi trường Arduino IDE. Bạn có thể điều khiển Web Server nhanh chóng dựa trên thiết bị di động hoặc bất kỳ thiết bị điện tử nào, khác miễn là có trình duyệt truy cập mạng cục bộ (local network).
Dưới đây, IoTZone sẽ hướng dẫn bạn cụ thể cách tạo Web Server và lập trình chi tiết từng bước! Cùng theo dõi nhé!
Giới thiệu dự án
Trước khi đi vào hướng dẫn chi tiết, mình sẽ giới thiệu đến bạn tổng quan về cách hoạt động của dự án này nhé:
- Chúng ta sẽ tạo ra một ESP32 Web Server điều khiển 2 đèn LED (ở chân GPIO 26 và GPIO 27)
- Truy cập vào ESP32 Web Server bằng cách nhập địa chỉ IP ESP32 trên trình duyệt Internet của mạng cục bộ
- Nhấn vào nút trên trang Web Server để thay đổi trạng thái của từng đèn LED
Đây chỉ là một ví dụ đơn giản về ESP32 Web Server, để bạn hình dung về cách điều khiển đầu ra của mạch. Trên thực tế, bạn có thể thay thế đèn LED bằng bất kỳ thiết bị đầu ra nào khác, chẳng hạn như relay hoặc quạt,… tùy thích!
Cài đặt tiện ích ESP32 trong Arduino IDE
Đầu tiên bạn cần cài đặt tiện ích ESP32 trong Arduino IDE theo hướng dẫn sau: Cách lập trình ESP32 bằng Arduino IDE (Windows, Linux, Mac OS X)
Chuẩn bị
Để thực hành dự án này, bạn cần chuẩn bị các linh kiện điện tử như sau:
- Mạch ESP32
- 2 Đèn LED 5mm
- 2 Điện trở ESP320 Ohm
- Breadboard
- Dây Jumper
Kết nối phần cứng
Bạn kết nối 2 đèn LED lần lượt vào các chân GPIO 26 và GPIO 27 của mạch theo hình dưới:
Lập trình
Mình đã chuẩn bị sẵn đoạn code bên dưới, bạn hãy sao chép chúng vào Arduino IDE nhé. Tuy nhiên, bạn khoan hãy upload code vào mạch ngay,bạn cần phải chỉnh một số thứ để code phù hợp với bạn.
void setup() { Serial.begin(115200); // Initialize the output variables as outputs pinMode(output26, OUTPUT); pinMode(output27, OUTPUT); // Set outputs to LOW digitalWrite(output26, LOW); digitalWrite(output27, LOW); // Connect to Wi-Fi network with SSID and password Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } // Print local IP address and start web server Serial.println(""); Serial.println("WiFi connected."); Serial.println("IP address: "); Serial.println(WiFi.localIP()); server.begin(); } void loop(){ WiFiClient client = server.available(); // Listen for incoming clients if (client) { // If a new client connects, currentTime = millis(); previousTime = currentTime; Serial.println("New Client."); // print a message out in the serial port String currentLine = ""; // make a String to hold incoming data from the client while (client.connected() && currentTime - previousTime <= timeoutTime) { // loop while the client's connected currentTime = millis(); if (client.available()) { // if there's bytes to read from the client, char c = client.read(); // read a byte, then Serial.write(c); // print it out the serial monitor header += c; if (c == '\n') { // if the byte is a newline character // if the current line is blank, you got two newline characters in a row. // that's the end of the client HTTP request, so send a response: if (currentLine.length() == 0) { // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK) // and a content-type so the client knows what's coming, then a blank line: client.println("HTTP/1.1 200 OK"); client.println("Content-type:text/html"); client.println("Connection: close"); client.println(); // turns the GPIOs on and off if (header.indexOf("GET /26/on") >= 0) { Serial.println("GPIO 26 on"); output26State = "on"; digitalWrite(output26, HIGH); } else if (header.indexOf("GET /26/off") >= 0) { Serial.println("GPIO 26 off"); output26State = "off"; digitalWrite(output26, LOW); } else if (header.indexOf("GET /27/on") >= 0) { Serial.println("GPIO 27 on"); output27State = "on"; digitalWrite(output27, HIGH); } else if (header.indexOf("GET /27/off") >= 0) { Serial.println("GPIO 27 off"); output27State = "off"; digitalWrite(output27, LOW); } // Display the HTML web page client.println("<!DOCTYPE html><html>"); client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"); client.println("<link rel=\"icon\" href=\"data:,\">"); // CSS to style the on/off buttons // Feel free to change the background-color and font-size attributes to fit your preferences client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}"); client.println(".button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px;"); client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}"); client.println(".button2 {background-color: #555555;}</style></head>"); // Web Page Heading client.println("<body><h1>ESP32 Web Server</h1>"); // Display current state, and ON/OFF buttons for GPIO 26 client.println("<p>GPIO 26 - State " + output26State + "</p>"); // If the output26State is off, it displays the ON button if (output26State=="off") { client.println("<p><a href=\"/26/on\"><button class=\"button\">ON</button></a></p>"); } else { client.println("<p><a href=\"/26/off\"><button class=\"button button2\">OFF</button></a></p>"); } // Display current state, and ON/OFF buttons for GPIO 27 client.println("<p>GPIO 27 - State " + output27State + "</p>"); // If the output27State is off, it displays the ON button if (output27State=="off") { client.println("<p><a href=\"/27/on\"><button class=\"button\">ON</button></a></p>"); } else { client.println("<p><a href=\"/27/off\"><button class=\"button button2\">OFF</button></a></p>"); } client.println("</body></html>"); // The HTTP response ends with another blank line client.println(); // Break out of the while loop break; } else { // if you got a newline, then clear currentLine currentLine = ""; } } else if (c != '\r') { // if you got anything else but a carriage return character, currentLine += c; // add it to the end of the currentLine } } } // Clear the header variable header = ""; // Close the connection client.stop(); Serial.println("Client disconnected."); Serial.println(""); } }
Hãy chỉnh sửa theo các bước sau:
Kết nối với mạng WiFi của bạn
Bạn cần sửa đổi thông tin WiFi của mình (gồm tên WiFi và mật khẩu tương ứng):
// Replace with your network credentials const char* ssid = "REPLACE_WITH_YOUR_SSID"; const char* password = "REPLACE_WITH_YOUR_PASSWORD";
Nạp code & tìm địa chỉ IP ESP32
Bây giờ, bạn có thể nạp code, ESP32 Web Server sẽ hoạt động.
Để tìm địa chỉ IP ES, bạn hãy click vào Serial Monitor và chọn tốc độ là 115200:
Nhấn vào nút ESPEN (reset). Khi mạch ESP32 kết nối với WiFI, thông tin về địa chỉ IP ESP sẽ hiển t hị trên Serial Monitor như hình dưới. Bạn hãy copy địa chỉ IP này, vì chúng ta sẽ cần dùng nó để truy cập vào ESP32 Web Server bằng điện thoại:
Truy cập vào ESP32 Web Server
Để truy cập vào ESP32 Web Server, bạn hãy mở trình duyệt của mình và dán địa chỉ IP ESP32 vào, bạn sẽ thấy màn hình hiển thị như hình dưới:
Nếu bạn mở Serial Monitor, bạn sẽ thấy được những gì đang diễn ra bên dưới. Mạch ESP nhận yêu cầu HTTP từ Client (trong trường hợp này là trình duyệt của bạn):
Điều khiển LED qua ESP32 Web Server
Bây giờ, bạn có thể thử nhấn các nút trên giao diện Web Server để kiểm tra xem chúng có hoạt động đúng không. Ví dụ, nhấn vào nustr điều khiển LED ở cổng GPIO 26:
Cùng lúc đó, bạn có thể mở Serial Monitor và quan sát cách hệ thống hoạt động bên dưới. Ví dụ, khi bạn click chuyển cổng GPIO sang ON (bật), mạch ESP32 nhận yêu cầu URL /26/on như hình:
Khi mạch ESP32 nhận yêu cầu đó, chúng sẽ bật đèn LED được gắn vào cổng GPIO thành ON và cập nhật trạng thái trên Web Server:
Giải thích chương trình
Trong phần này, mình sẽ giải thích chi tiết cách chương trình hoạt động cho bạn nhé!
Đầu tiên, chúng ta cần tạo thư viện WiFi:
#include <WiFi.h>
Sau đó, bạn cần chèn thông tin WiFi của mình (gồm tên và mật khẩu):
const char* ssid = ""; const char* password = "";
Thiết lập ESP32 Web Server ở cổng 80:
WiFiServer server(80);
Tạo một biến để lưu trữ tiêu đề của các yêu cầu HTTP:
String header;
Tạo các biến phụ trợ để lưu trữ trạng thái hiện tại của các đầu ra output (Trong trường hợp này là đèn LED). Nếu bạn cần điều khiển nhiều output hơn, bạn cần tạo nhiều biến hơn để lưu các trạng thái của nó:
String output26State = "off"; String output27State = "off";
Bạn cần gán chân GPIO cho từng output của mình. Vì trong dự án EPS32 Web Server này, mình dùng chân GPIO 26 và GPIO 27 nên đoạn code sẽ như sau (bạn có thể sử dụng bất kỳ chân GPIO nào khác tùy thích, miễn là chúng phù hợp):
const int output26 = 26; const int output27 = 27;
Lập trình setup()
Trong phần setup(), đầu tiên bạn hãy tạo giao tiếp nối tiếp ở tốc độ truyền 115200, để phục vụ các hoạt động quan sát và gỡ lỗi sau này:
Serial.begin(115200);
Gán các chân GPIO và đặt chúng ở mức LOW:
// Initialize the output variables as outputs pinMode(output26, OUTPUT); pinMode(output27, OUTPUT); // Set outputs to LOW digitalWrite(output26, LOW); digitalWrite(output27, LOW);
Kết nối vào mạng WiFi đã có, chờ đợi kết nối thành công thì in địa chỉ IP ESP lên màn hình Serial Monitor:
// Connect to Wi-Fi network with SSID and password Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } // Print local IP address and start web server Serial.println(""); Serial.println("WiFi connected."); Serial.println("IP address: "); Serial.println(WiFi.localIP()); server.begin();
Lập trình loop()
Ở đây, chúng ta sẽ lập trình các hoạt động sau khi điện thoại kết nối thành công với ESP32 Web Server.
Đầu tiên, mạch ESP32 luôn ở trạng thái nhận các yêu cầu của máy khách Client (trong trường hợp này là trình duyệt trên điện thoại của bạn):
WiFiClient client = server.available(); // Listen for incoming clients
Khi nhận được yêu cầu, chúng ta cần lưu dữ liệu được gửi đến. Chỉ cần máy khách vẫn được kết nối, vòng lặp while tiếp theo sẽ chạy. Mình khuyên bạn không nên chỉnh sửa các dòng code đó, trừ khi bạn hiểu rõ về code:
if (client) { // If a new client connects, Serial.println("New Client."); // print a message out in the serial port String currentLine = ""; // make a String to hold incoming data from the client while (client.connected()) { // loop while the client's connected if (client.available()) { // if there's bytes to read from the client, char c = client.read(); // read a byte, then Serial.write(c); // print it out the serial monitor header += c; if (c == '\n') { // if the byte is a newline character // if the current line is blank, you got two newline characters in a row. / that's the end of the client HTTP request, so send a response: if (currentLine.length() == 0) { // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK) // and a content-type so the client knows what's coming, then a blank line: client.println("HTTP/1.1 200 OK"); client.println("Content-type:text/html"); client.println("Connection: close"); client.println()
Tạo điều kiện if và else để xác định nút nào đã được nhấn trên ESP32 Web Server, sau đó hiển thị kết quả đầu ra tương ứng. Như đã trình bày, mỗi nút khác nhau khi được nhấn sẽ gửi các yêu cầu trên các URL khác nhau:
// turns the GPIOs on and off if (header.indexOf("GET /26/on") >= 0) { Serial.println("GPIO 26 on"); output26State = "on"; digitalWrite(output26, HIGH); } else if (header.indexOf("GET /26/off") >= 0) { Serial.println("GPIO 26 off"); output26State = "off"; digitalWrite(output26, LOW); } else if (header.indexOf("GET /27/on") >= 0) { Serial.println("GPIO 27 on"); output27State = "on"; digitalWrite(output27, HIGH); } else if (header.indexOf("GET /27/off") >= 0) { Serial.println("GPIO 27 off"); output27State = "off"; digitalWrite(output27, LOW); }
Như đã ví dụ, nếu bạn click bật cổng GPIO trên giao diện ESP32 Web Server, mạch ESP32 sẽ nhận được yêu cầu /26/ON trên URL (chúng ta có thể quan sát điều này thông qua Serial Monitor).
Do đó, chúng ta sẽ lập trình như sau: Khi phát hiện GET /26/ON trên header, hãy thay đổi trạng thái output của cổng 26 (output26state) thành ON. Điều này đồng nghĩa với việc mạch ESP32 bật đèn LED.
Với các nút nhấn khacs thì cách hoạt động cũng tương tự. Do đó, bạn có thể tạo ra nhiều output hơn dựa trên đoạn code này, chỉ cần chỉnh sửa các thông tin chính như cổng GPIO, trạng thái.
Tạo trang Web HTML
Bước cuối cùng, chúng ta cần tạo một Web HTML. Mạch ESP32 sẽ phản hồi trạng thái output tới trình duyệt kèm theo các đoạn code HTML để xây dựng Website.
Chúng ta gửi Web Server đến máy khách thông qua dòng lệnh client.println(), với thông tin gồm những nội dung bạn muốn hiển thị.
Điều đầu tiên, chúng ta cần phải dùng dòng lệnh sau, để hệ thống biết chúng ta đang gửi HTML:
<!DOCTYPE HTML><html>
Cho phép ESP32 Web Server đưa ra phản hồi nhanh chóng dù là trên bất kỳ trình duyệt Internet nào:
client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
Ngăn chặn các yêu cầu trên favicon:
client.println("<link rel=\"icon\" href=\"data:,\">");
Thiết lập giao diện Web Server
Chúng ta sẽ dùng CSS để cấu hình giao diện và trang trí các nút cho Web Server đẹp mắt nhé!
Trong ví dụ này, mình sử dụng font chữ Helvetica, chọn các nút nhấn thành hình khối và cho căn chỉnh chính giữa màn hình. Bạn có thể tùy chỉnh sao phù hợp với thẩm mỹ của bạn nhé:
client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
Mình chỉ đang tạo các nút đơn giản dựa trên font màu #4CAF50 và không có viền, văn bản trên nút sẽ có màu trắng và padding là 16px 40px. Ngoài ra, mình không trang trí văn bản gì khác, chỉ xác định kích thước font, căn lề:
client.println(".button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px;"); client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
Mình bắt đầu tạo nút thứ 2, với lối trang trí tương tự nút đầu tiên nhưng có màu sắc khác để phân biệt:
client.println(".button2 {background-color: #555555;}</style></head>");
Đặt tiêu đề cho ESP32 Web Server
Như trong ví dụ này, mình đang đặt tiêu đề là ESP32 Web Server. Bạn có thể đổi tiêu đề này thành bất kỳ dòng chữ nào mà bạn thích:
// Web Page Heading client.println("<h1>ESP32 Web Server</h1>");
Hiển thị các nút và trạng thái output
Chúng ta cần viết đoạn code để ESP32 Web Server hiển thị trạng thái tương ứng của các đoạn LED. Dưới đây, mình sử dụng biến output26State để cập nhật trạng thái của đèn LED:
client.println("<p>GPIO 26 - State " + output26State + "</p>");
Sau đó, chúng ta cho nút nhấn hiển thị ON hoặc OFF, phụ thuộc vào trạng thái của output. Nếu trạng thái output hiện tại là tắt thì chúng ta hiển thị nút ON, nếu không thì chúng ta hiển thị nút OFF:
if (output26State=="off") { client.println("<p><a href=\"/26/on\"><button class=\"button\">ON</button></a></p>"); } else { client.println("<p><a href=\"/26/off\"><button class=\"button button2\">OFF</button></a></p>"); }
Chúng ta sẽ thực hiện tương tự với đèn LED kết nối cổng GPIO 27 nhé.
Ngắt kết nối
Cuối cùng, sau khi phản hồi xong, chúng ta clear biến header và ngưng kết nối:
// Clear the header variable header = ""; // Close the connection client.stop();
Lời kết
Trong hướng dẫn trên, mình đã hướng dẫn bạn cách tạo ESP32 Web Server, cụ thể chúng ta đã điều khiển 2 đèn LED. Tuy nhiên, bạn hãy thử sáng tạo và thay thế đèn LED thành những output khác như Relay hoặc bất kỳ thiết bị đầu ra nào bạn muốn nhé!
Chúc các bạn thành công.