Arduino Máy chủ Web Đa trang

Trong hướng dẫn này, chúng ta sẽ tìm hiểu cách biến Arduino thành một máy chủ web có thể xử lý nhiều trang cùng lúc, ví dụ như index.html, temperature.html, led.html, error_404.html, và error_405.html... Nội dung của từng trang, bao gồm HTML, CSS và JavaScript, sẽ được lưu trữ ở các file riêng biệt trên Arduino IDE. Bằng cách truy cập Arduino Web Server từ trình duyệt web trên máy tính hoặc điện thoại thông minh của bạn, bạn sẽ có thể xem và điều khiển cảm biến và các thiết bị được kết nối với Arduino thông qua web. Ngoài ra, máy chủ web sẽ được thiết kế để chấp nhận liên kết có hoặc không có phần mở rộng .html.

trình duyệt web Arduino

Bằng cách làm theo hướng dẫn này, bạn sẽ có thể biến Arduino của bạn thành một máy chủ web với một số tính năng thú vị:

Nghe có vẻ phức tạp, nhưng đừng lo! Hướng dẫn này cung cấp chỉ dẫn từng bước, và mã nguồn được thiết kế thân thiện với người mới bắt đầu, đảm bảo bạn có thể hiểu dễ dàng và tự tạo máy chủ web Arduino cho riêng mình.

Phần cứng cần chuẩn bị

1×Arduino UNO R4 WiFi
1×Alternatively, DIYables STEM V4 IoT
1×(Tùy chọn) DIYables STEM V4 IoT
1×USB Cable Type-A to Type-C (for USB-A PC)
1×USB Cable Type-C to Type-C (for USB-C PC)
1×(Khuyến nghị) Screw Terminal Block Shield for Arduino UNO R4
1×(Khuyến nghị) Breadboard Shield for Arduino UNO R4
1×(Khuyến nghị) Enclosure for Arduino UNO R4
1×(Khuyến nghị) Power Splitter for Arduino UNO R4
1×(Khuyến nghị) Prototyping Base Plate & Breadboard Kit for Arduino UNO

Or you can buy the following kits:

1×DIYables STEM V4 IoT Starter Kit (Arduino included)
1×DIYables Sensor Kit (30 sensors/displays)
1×DIYables Sensor Kit (18 sensors/displays)

Về Arduino Uno R4 và máy chủ web

Nếu bạn chưa quen với Arduino Uno R4 và Web Server (bao gồm sơ đồ chân, cách nó hoạt động và lập trình), bạn có thể tìm hiểu về chúng thông qua các bài hướng dẫn sau:

Cách một máy chủ web trên Arduino hoạt động

Khi trình duyệt web gửi một yêu cầu HTTP tới bo mạch Arduino, Arduino cần được lập trình để thực hiện các tác vụ sau:

  • Tạo một máy chủ web có thể lắng nghe các yêu cầu HTTP từ trình duyệt web.
  • Đọc dòng đầu tiên của tiêu đề HTTP khi nó nhận được một yêu cầu HTTP.
  • Định tuyến các yêu cầu dựa trên dòng đầu tiên của yêu cầu HTTP để xác định những trang web mà Arduino nên trả về.
  • (Tùy chọn) Phân tích tiêu đề HTTP của yêu cầu để nhận diện các lệnh điều khiển do người dùng gửi.
  • (Tùy chọn) Điều khiển các thiết bị được kết nối với Arduino dựa trên các lệnh điều khiển đã nhận.
  • Gửi một phản hồi HTTP trở lại trình duyệt web, trong đó có:
    • Một tiêu đề phản hồi HTTP.
    • Nội dung thân phản hồi HTTP, chứa nội dung HTML và (nếu có) dữ liệu cảm biến hoặc trạng thái thiết bị.

    Thông qua việc thực hiện những nhiệm vụ này, Arduino có thể xử lý hiệu quả các yêu cầu HTTP và cung cấp các phản hồi phù hợp cho trình duyệt web, cho phép điều khiển và tương tác dựa trên web với các thiết bị được kết nối với Arduino.

    Chức năng định tuyến là nhiệm vụ quan trọng nhất, và nó sẽ được giải thích chi tiết. Các phần khác được đề cập trong hướng dẫn Arduino - Máy chủ web. Khi bạn đã hiểu thuật toán định tuyến, chúng ta sẽ tiến hành xem toàn bộ mã nguồn cho một máy chủ web có nhiều trang.

Máy chủ Web Arduino - Thuật toán định tuyến

Trước khi viết mã cho hàm định tuyến, bạn nên tạo một danh sách các trang web và các phương thức HTTP tương ứng sẽ có sẵn trên Arduino. Trong hướng dẫn này, chúng ta sẽ chỉ hỗ trợ phương thức GET. Tuy nhiên, bạn có thể dễ dàng mở rộng nó để bao gồm các phương thức HTTP khác nếu cần. Dưới đây là một danh sách ví dụ:

  • Lấy trang chủ
  • Lấy trang nhiệt độ
  • Lấy trang cửa
  • Lấy trang đèn LED

Sau đó, bạn cần tạo một danh sách các trường tiêu đề của yêu cầu HTTP ở dòng đầu tiên tương ứng với danh sách trang:

  • GET trang chủ:
    • GET /
    • GET /index.html
  • GET trang nhiệt độ:
    • GET /temperature.html
  • GET trang cửa:
    • GET /door
  • GET trang đèn LED:
    • GET /led.html

    Tóm lại, chúng ta có danh sách sau:

    GET / GET /index.html GET /temperature.html GET /door.html GET /led.html

Mã Arduino - Máy chủ web đa trang

Dưới đây là mã Arduino đầy đủ tạo ra một máy chủ web có nhiều trang. Để đơn giản hóa, nội dung HTML cho từng trang rất đơn giản và được nhúng trực tiếp vào mã Arduino. Ở phần tiếp theo, chúng ta sẽ học cách tách nội dung HTML cho từng trang thành các tệp riêng biệt, làm cho mã nguồn trở nên gọn gàng và dễ quản lý hơn.

/* * Mã Arduino này được phát triển bởi newbiely.vn * Mã Arduino này được cung cấp để sử dụng công khai, không có ràng buộc. * Để xem hướng dẫn chi tiết và sơ đồ kết nối, vui lòng truy cập: * https://newbiely.vn/tutorials/arduino/arduino-web-server-multiple-pages */ #include <UnoR4WiFi_WebServer.h> // WiFi credentials const char WIFI_SSID[] = "YOUR_WIFI_SSID"; // change your network SSID (name) const char WIFI_PASSWORD[] = "YOUR_WIFI_PASSWORD"; // change your network password // Create web server instance UnoR4WiFi_WebServer server; float getTemperature() { // YOUR SENSOR IMPLEMENTATION HERE // simulate the temperature value float temp_x100 = random(0, 10000); // a ramdom value from 0 to 10000 return temp_x100 / 100; // return the simulated temperature value from 0 to 100 in float } // Page handlers void handleHome(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { server.sendResponse(client, "This is home page"); } void handleTemperature(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { server.sendResponse(client, "This is temperature page"); } void handleDoor(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { server.sendResponse(client, "This is door page"); } void handleLed(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { server.sendResponse(client, "This is LED page"); } void handleNotFound(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { server.sendResponse(client, "Page Not Found"); } void setup() { Serial.begin(9600); delay(1000); Serial.println("Arduino Uno R4 WiFi - Multi-Page Web Server"); // Connect to WiFi Serial.print("Connecting to "); Serial.println(WIFI_SSID); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(" connected!"); Serial.print("IP address: "); Serial.println(WiFi.localIP()); // Configure routes server.addRoute("/", handleHome); server.addRoute("/index", handleHome); server.addRoute("/index.html", handleHome); server.addRoute("/temperature", handleTemperature); server.addRoute("/temperature.html", handleTemperature); server.addRoute("/door", handleDoor); server.addRoute("/door.html", handleDoor); server.addRoute("/led", handleLed); server.addRoute("/led.html", handleLed); // Set custom 404 handler server.setNotFoundHandler(handleNotFound); // Start server server.begin(); Serial.println("\n=== Web Server Ready! ==="); Serial.print("Visit: http://"); Serial.println(WiFi.localIP()); } void loop() { server.handleClient(); }

Hướng dẫn từng bước

  • Nếu đây là lần đầu tiên bạn sử dụng Arduino Uno R4, hãy xem Cách bắt đầu với Arduino UNO R4.
  • Mở Trình quản lý thư viện bằng cách nhấp vào biểu tượng ở bên trái của Arduino IDE.
  • Tìm kiếm Máy chủ Web cho Arduino Uno R4 WiFi và xác định thư viện Máy chủ Web do DIYables tạo ra.
  • Nhấp vào nút Cài đặt để thêm thư viện Máy chủ Web.
thư viện máy chủ web cho Arduino UNO R4
  • Sao chép mã ở trên và mở bằng Arduino IDE
  • Thay đổi thông tin wifi (SSID và mật khẩu) trong mã cho phù hợp với bạn
  • Nhấn nút Tải lên trên Arduino IDE để tải mã lên cho Arduino
  • Mở Serial Monitor
  • Kiểm tra kết quả trên Serial Monitor
COM6
Send
Connecting to YOUR_WIFI_SSID connected! IP address: 192.168.0.254 Starting web server on IP: 192.168.0.254 === Web Server Ready! === Visit: http://192.168.0.254
Autoscroll Show timestamp
Clear output
9600 baud  
Newline  
  • Bạn sẽ thấy một địa chỉ IP trên Màn hình Serial, ví dụ: 192.168.0.2
  • Gõ lần lượt danh sách sau vào thanh địa chỉ của trình duyệt trên điện thoại thông minh hoặc PC của bạn.
192.168.0.2 192.168.0.2/index 192.168.0.2/index.html 192.168.0.2/led 192.168.0.2/led.html 192.168.0.2/door 192.168.0.2/door.html 192.168.0.2/temperature 192.168.0.2/temperature.html 192.168.0.2/blabla 192.168.0.2/blabla.html
  • Lưu ý rằng bạn cần thay đổi 192.168.0.2 thành địa chỉ IP bạn nhận được trên Serial Monitor.
  • Bạn sẽ thấy các trang sau: trang chủ, trang đèn LED, trang cửa, trang nhiệt độ và trang Không tìm thấy.
  • Bạn cũng có thể kiểm tra đầu ra trên Serial Monitor.

Đoạn mã trước có nội dung HTML rất đơn giản cho mỗi trang. Tuy nhiên, nếu muốn tạo giao diện bắt mắt với nhiều mã HTML, mã có thể trở nên lớn và rối. Để đơn giản hóa, chúng ta sẽ học cách tách HTML khỏi mã Arduino. Điều này cho phép chúng ta lưu trữ HTML trong các tệp riêng biệt, giúp quản lý và làm việc với nó dễ dàng hơn.

Mã Arduino - Máy chủ web đa trang hoàn chỉnh

  • Mở Arduino IDE.
  • Tạo một sketch mới và đặt cho nó một tên, ví dụ, ArduinoWebServer.ino.
  • Sao chép mã được cung cấp và dán vào tệp đó.
/* * Mã Arduino này được phát triển bởi newbiely.vn * Mã Arduino này được cung cấp để sử dụng công khai, không có ràng buộc. * Để xem hướng dẫn chi tiết và sơ đồ kết nối, vui lòng truy cập: * https://newbiely.vn/tutorials/arduino/arduino-web-server-multiple-pages */ #include <UnoR4WiFi_WebServer.h> #include "index.h" #include "temperature.h" #include "door.h" #include "led.h" #include "error_404.h" // WiFi credentials const char WIFI_SSID[] = "YOUR_WIFI_SSID"; // change your network SSID (name) const char WIFI_PASSWORD[] = "YOUR_WIFI_PASSWORD"; // change your network password // Create web server instance UnoR4WiFi_WebServer server; float getTemperature() { // YOUR SENSOR IMPLEMENTATION HERE // simulate the temperature value float temp_x100 = random(0, 10000); // a ramdom value from 0 to 10000 return temp_x100 / 100; // return the simulated temperature value from 0 to 100 in float } // Page handlers void handleHome(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { server.sendResponse(client, HTML_CONTENT_HOME); } void handleTemperature(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { String html = String(HTML_CONTENT_TEMPERATURE); html.replace("TEMPERATURE_MARKER", String(getTemperature(), 2)); server.sendResponse(client, html.c_str()); } void handleDoor(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { String html = String(HTML_CONTENT_DOOR); html.replace("DOOR_STATE_MARKER", "OPENED"); server.sendResponse(client, html.c_str()); } void handleLed(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { String html = String(HTML_CONTENT_LED); html.replace("LED_STATE_MARKER", "OFF"); server.sendResponse(client, html.c_str()); } void handleNotFound(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { String html = String(HTML_CONTENT_404 ); server.sendResponse(client, html.c_str()); } void setup() { Serial.begin(9600); delay(1000); Serial.println("Arduino Uno R4 WiFi - Multi-Page Web Server"); // Connect to WiFi Serial.print("Connecting to "); Serial.println(WIFI_SSID); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(" connected!"); Serial.print("IP address: "); Serial.println(WiFi.localIP()); // Configure routes server.addRoute("/", handleHome); server.addRoute("/index", handleHome); server.addRoute("/index.html", handleHome); server.addRoute("/temperature", handleTemperature); server.addRoute("/temperature.html", handleTemperature); server.addRoute("/door", handleDoor); server.addRoute("/door.html", handleDoor); server.addRoute("/led", handleLed); server.addRoute("/led.html", handleLed); // Set custom 404 handler server.setNotFoundHandler(handleNotFound); // Start server server.begin(); Serial.println("\n=== Web Server Ready! ==="); Serial.print("Visit: http://"); Serial.println(WiFi.localIP()); } void loop() { server.handleClient(); }
  • Thay đổi thông tin WiFi (SSID và mật khẩu) trong mã sang của bạn
  • Tạo tệp index.h trên Arduino IDE bằng cách:
    • Hoặc nhấp vào nút ngay bên dưới biểu tượng Serial Monitor và chọn Tab Mới, hoặc dùng phím Ctrl+Shift+N
    Arduino ide 2 thêm tệp
    • Nhập tên tệp tin index.h và nhấn nút OK
    Arduino ide 2 thêm tệp index.h
    • Sao chép đoạn mã dưới đây và dán vào tệp index.h.
    /* * Mã Arduino này được phát triển bởi newbiely.vn * Mã Arduino này được cung cấp để sử dụng công khai, không có ràng buộc. * Để xem hướng dẫn chi tiết và sơ đồ kết nối, vui lòng truy cập: * https://newbiely.vn/tutorials/arduino/arduino-web-server-multiple-pages */ const char *HTML_CONTENT_HOME = R""""( <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="icon" href="data:,"> <title>Home Page</title> </head> <body> <h1>Welcome to the Home Page</h1> <ul> <li><a href="/led">LED Page</a></li> <li><a href="/temperature">Temperature Page</a></li> <li><a href="/door">Door Page</a></li> </ul> </body> </html> )"""";
    • Tương tự, tạo tệp temperature.h trên Arduino IDE với nội dung sau.
    /* * Mã Arduino này được phát triển bởi newbiely.vn * Mã Arduino này được cung cấp để sử dụng công khai, không có ràng buộc. * Để xem hướng dẫn chi tiết và sơ đồ kết nối, vui lòng truy cập: * https://newbiely.vn/tutorials/arduino/arduino-web-server-multiple-pages */ const char *HTML_CONTENT_TEMPERATURE = R""""( <!DOCTYPE html> <html> <head> <title>Arduino - Web Temperature</title> <meta name="viewport" content="width=device-width, initial-scale=0.7, maximum-scale=0.7"> <meta charset="utf-8"> <link rel="icon" href="https://diyables.io/images/page/diyables.svg"> <style> body { font-family: "Georgia"; text-align: center; font-size: width/2pt;} h1 { font-weight: bold; font-size: width/2pt;} h2 { font-weight: bold; font-size: width/2pt;} button { font-weight: bold; font-size: width/2pt;} </style> <script> var cvs_width = 200, cvs_height = 450; function init() { var canvas = document.getElementById("cvs"); canvas.width = cvs_width; canvas.height = cvs_height + 50; var ctx = canvas.getContext("2d"); ctx.translate(cvs_width/2, cvs_height - 80); update_view(TEMPERATURE_MARKER); } function update_view(temp) { var canvas = document.getElementById("cvs"); var ctx = canvas.getContext("2d"); var radius = 70; var offset = 5; var width = 45; var height = 330; ctx.clearRect(-cvs_width/2, -350, cvs_width, cvs_height); ctx.strokeStyle="blue"; ctx.fillStyle="blue"; //5-step Degree var x = -width/2; ctx.lineWidth=2; for (var i = 0; i <= 100; i+=5) { var y = -(height - radius)*i/100 - radius - 5; ctx.beginPath(); ctx.lineTo(x, y); ctx.lineTo(x - 20, y); ctx.stroke(); } //20-step Degree ctx.lineWidth=5; for (var i = 0; i <= 100; i+=20) { var y = -(height - radius)*i/100 - radius - 5; ctx.beginPath(); ctx.lineTo(x, y); ctx.lineTo(x - 25, y); ctx.stroke(); ctx.font="20px Georgia"; ctx.textBaseline="middle"; ctx.textAlign="right"; ctx.fillText(i.toString(), x - 35, y); } // shape ctx.lineWidth=16; ctx.beginPath(); ctx.arc(0, 0, radius, 0, 2 * Math.PI); ctx.stroke(); ctx.beginPath(); ctx.rect(-width/2, -height, width, height); ctx.stroke(); ctx.beginPath(); ctx.arc(0, -height, width/2, 0, 2 * Math.PI); ctx.stroke(); ctx.fillStyle="#e6e6ff"; ctx.beginPath(); ctx.arc(0, 0, radius, 0, 2 * Math.PI); ctx.fill(); ctx.beginPath(); ctx.rect(-width/2, -height, width, height); ctx.fill(); ctx.beginPath(); ctx.arc(0, -height, width/2, 0, 2 * Math.PI); ctx.fill(); ctx.fillStyle="#ff1a1a"; ctx.beginPath(); ctx.arc(0, 0, radius - offset, 0, 2 * Math.PI); ctx.fill(); temp = Math.round(temp * 100) / 100; var y = (height - radius)*temp/100.0 + radius + 5; ctx.beginPath(); ctx.rect(-width/2 + offset, -y, width - 2*offset, y); ctx.fill(); ctx.fillStyle="red"; ctx.font="bold 34px Georgia"; ctx.textBaseline="middle"; ctx.textAlign="center"; ctx.fillText(temp.toString() + "°C", 0, 100); } window.onload = init; </script> </head> <body> <h1>Arduino - Web Temperature</h1> <canvas id="cvs"></canvas> </body> </html> )"""";
    • Tương tự, hãy tạo tệp door.h trên Arduino IDE với nội dung sau.
    /* * Mã Arduino này được phát triển bởi newbiely.vn * Mã Arduino này được cung cấp để sử dụng công khai, không có ràng buộc. * Để xem hướng dẫn chi tiết và sơ đồ kết nối, vui lòng truy cập: * https://newbiely.vn/tutorials/arduino/arduino-web-server-multiple-pages */ const char *HTML_CONTENT_DOOR = R""""( <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="icon" href="data:,"> <title>Door Page</title> </head> <body> <h1>Door Page</h1> <p>Door State: <span style="color: red;">DOOR_STATE_MARKER</span></p> </body> </html> )"""";
    • Tương tự, hãy tạo tệp led.h trong Arduino IDE với nội dung sau.
    /* * Mã Arduino này được phát triển bởi newbiely.vn * Mã Arduino này được cung cấp để sử dụng công khai, không có ràng buộc. * Để xem hướng dẫn chi tiết và sơ đồ kết nối, vui lòng truy cập: * https://newbiely.vn/tutorials/arduino/arduino-web-server-multiple-pages */ const char *HTML_CONTENT_LED = R""""( <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="icon" href="data:,"> <title>LED Page</title> </head> <body> <h1>LED Page</h1> <p>LED State: <span style="color: red;">LED_STATE_MARKER</span></p> </body> </html> )"""";
    • Tương tự, tạo tệp error_404.h trên Arduino IDE với nội dung sau.
    /* * Mã Arduino này được phát triển bởi newbiely.vn * Mã Arduino này được cung cấp để sử dụng công khai, không có ràng buộc. * Để xem hướng dẫn chi tiết và sơ đồ kết nối, vui lòng truy cập: * https://newbiely.vn/tutorials/arduino/arduino-web-server-multiple-pages */ const char *HTML_CONTENT_404 = R""""( <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="icon" href="data:,"> <title>404 - Page Not Found</title> <style> h1 {color: #ff4040;} </style> </head> <body> <h1>404</h1> <p>Oops! The page you are looking for could not be found on Arduino Web Server.</p> <p>Please check the URL or go back to the <a href="/">homepage</a>.</p> <p>Or check <a href="https://arduinogetstarted.com/tutorials/arduino-web-server-multiple-pages"> Arduino Web Server</a> tutorial.</p> </body> </html> )"""";
    • Bây giờ bạn có nhiều tệp tin trên Arduino IDE như dưới đây:
    Arduino ide 2 nhiều tệp tin
    • Nhấn nút Tải lên trên Arduino IDE để tải mã lên Arduino
    • Truy cập lần lượt các trang web của bảng mạch Arduino bằng trình duyệt web như trước. Bạn sẽ thấy tất cả các trang web như dưới đây:
    nhiều trang web về Arduino

    ※ Lưu ý:

    • Nếu bạn thực hiện các thay đổi đối với nội dung HTML bên trong tệp index.h nhưng không sửa đổi bất kỳ điều gì trong tệp ArduinoWebServer.ino, Arduino IDE sẽ không làm mới hoặc cập nhật nội dung HTML khi bạn biên dịch và tải mã lên ESP32.
    • Để bắt buộc Arduino IDE cập nhật nội dung HTML trong trường hợp này, bạn cần thực hiện một sửa đổi trong tệp ArduinoWebServer.ino. Ví dụ, bạn có thể thêm một dòng trống hoặc chèn một nhận xét. Hành động này kích hoạt IDE nhận ra rằng đã có sự thay đổi trong dự án, đảm bảo nội dung HTML được cập nhật của bạn được đưa vào tải lên.