Ví dụ này trình bày cách sử dụng nhiều ứng dụng web đồng thời với thư viện DIYables WebApps. Nó minh họa việc tích hợp một số giao diện web tương tác - chẳng hạn như giám sát, điều khiển và giao tiếp - trong một dự án duy nhất. Được thiết kế cho Arduino Uno R4 WiFi và nền tảng DIYables STEM V4 IoT, ví dụ này lý tưởng để học cách kết hợp và quản lý nhiều tính năng dựa trên web cùng lúc, cung cấp nền tảng vững chắc cho các dự án IoT nâng cao.
| 1 | × | Arduino UNO R4 WiFi | | |
| 1 | × | Alternatively, DIYables STEM V4 IoT | | |
| 1 | × | (Tùy chọn) DIYables STEM V4 IoT | | |
| 1 | × | Cáp USB Type-C | | |
| 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) | | |
Thực hiện theo từng bước hướng dẫn sau:
Kết nối bo Arduino Uno R4/DIYables STEM V4 IoT với máy tính bằng cáp USB.
Khởi động Arduino IDE trên máy tính.
Chọn bo Arduino Uno R4 phù hợp (ví dụ: Arduino Uno R4 WiFi) và cổng COM.
Điều hướng đến biểu tượng Libraries trên thanh bên trái của Arduino IDE.
Tìm kiếm "DIYables WebApps", sau đó tìm thư viện DIYables WebApps của DIYables
Nhấp nút Install để cài đặt thư viện.


#include <DIYablesWebApps.h>
const char WIFI_SSID[] = "YOUR_WIFI_SSID";
const char WIFI_PASSWORD[] = "YOUR_WIFI_PASSWORD";
UnoR4ServerFactory factory;
DIYablesWebAppServer webAppsServer(factory, 80, 81);
DIYablesHomePage homePage;
DIYablesWebMonitorPage webMonitorPage;
DIYablesWebSliderPage webSliderPage;
DIYablesWebJoystickPage webJoystickPage(false, 5);
DIYablesWebRotatorPage webRotatorPage(ROTATOR_MODE_CONTINUOUS);
DIYablesWebAnalogGaugePage webAnalogGaugePage(0.0, 100.0, "%");
DIYablesWebTablePage webTablePage;
int currentSlider1 = 64;
int currentSlider2 = 128;
int currentJoystickX = 0;
int currentJoystickY = 0;
int currentRotatorAngle = 0;
float currentGaugeValue = 50.0;
void setup() {
Serial.begin(9600);
delay(1000);
Serial.println("DIYables WebApp - Multiple Apps Example");
webAppsServer.addApp(&homePage);
webAppsServer.addApp(&webMonitorPage);
webAppsServer.addApp(&webSliderPage);
webAppsServer.addApp(&webJoystickPage);
webAppsServer.addApp(&webRotatorPage);
webAppsServer.addApp(&webAnalogGaugePage);
webAppsServer.addApp(&webTablePage);
webAppsServer.setNotFoundPage(DIYablesNotFoundPage());
webTablePage.addRow("Arduino Status");
webTablePage.addRow("WiFi Connected");
webTablePage.addRow("Uptime");
webTablePage.addRow("Slider 1");
webTablePage.addRow("Slider 2");
webTablePage.addRow("Joystick X");
webTablePage.addRow("Joystick Y");
webTablePage.addRow("Rotator Angle");
webTablePage.addRow("Gauge Value");
if (!webAppsServer.begin(WIFI_SSID, WIFI_PASSWORD)) {
while (1) {
Serial.println("Failed to start WebApp server!");
delay(1000);
}
}
setupCallbacks();
}
void setupCallbacks() {
webMonitorPage.onWebMonitorMessage([](const String& message) {
Serial.println("Web Monitor: " + message);
webMonitorPage.sendToWebMonitor("Arduino received: " + message);
});
webSliderPage.onSliderValueFromWeb([](int slider1, int slider2) {
currentSlider1 = slider1;
currentSlider2 = slider2;
Serial.print("Slider 1: ");
Serial.print(slider1);
Serial.print(", Slider 2: ");
Serial.println(slider2);
webTablePage.sendValueUpdate("Slider 1", String(slider1));
webTablePage.sendValueUpdate("Slider 2", String(slider2));
currentGaugeValue = map(slider1, 0, 255, 0, 100);
webAnalogGaugePage.sendToWebAnalogGauge(currentGaugeValue);
char gaugeStr[16];
snprintf(gaugeStr, sizeof(gaugeStr), "%.1f%%", currentGaugeValue);
webTablePage.sendValueUpdate("Gauge Value", String(gaugeStr));
});
webSliderPage.onSliderValueToWeb([]() {
webSliderPage.sendToWebSlider(currentSlider1, currentSlider2);
});
webJoystickPage.onJoystickValueFromWeb([](int x, int y) {
currentJoystickX = x;
currentJoystickY = y;
Serial.print("Joystick - X: ");
Serial.print(x);
Serial.print(", Y: ");
Serial.println(y);
Serial.print(x);
Serial.print(", Y: ");
Serial.println(y);
webTablePage.sendValueUpdate("Joystick X", String(x));
webTablePage.sendValueUpdate("Joystick Y", String(y));
}
});
webJoystickPage.onJoystickValueToWeb([]() {
webJoystickPage.sendToWebJoystick(currentJoystickX, currentJoystickY);
});
webRotatorPage.onRotatorAngleFromWeb([](float angle) {
currentRotatorAngle = (int)angle;
Serial.println("Rotator angle: " + String(angle) + "°");
webTablePage.sendValueUpdate("Rotator Angle", String(angle, 0) + "°");
});
webAnalogGaugePage.onGaugeValueRequest([]() {
webAnalogGaugePage.sendToWebAnalogGauge(currentGaugeValue);
});
webTablePage.onTableValueRequest([]() {
webTablePage.sendValueUpdate("Arduino Status", "Running");
webTablePage.sendValueUpdate("WiFi Connected", "Yes");
webTablePage.sendValueUpdate("Uptime", "0 seconds");
webTablePage.sendValueUpdate("Slider 1", String(currentSlider1));
webTablePage.sendValueUpdate("Slider 2", String(currentSlider2));
webTablePage.sendValueUpdate("Joystick X", String(currentJoystickX));
webTablePage.sendValueUpdate("Joystick Y", String(currentJoystickY));
webTablePage.sendValueUpdate("Rotator Angle", String(currentRotatorAngle) + "°");
webTablePage.sendValueUpdate("Gauge Value", String(currentGaugeValue, 1) + "%");
});
}
void loop() {
webAppsServer.loop();
static unsigned long lastUptimeUpdate = 0;
if (millis() - lastUptimeUpdate > 5000) {
lastUptimeUpdate = millis();
unsigned long uptimeSeconds = millis() / 1000;
String uptimeStr = String(uptimeSeconds) + " seconds";
if (uptimeSeconds >= 60) {
uptimeStr = String(uptimeSeconds / 60) + "m " + String(uptimeSeconds % 60) + "s";
}
webTablePage.sendValueUpdate("Uptime", uptimeStr);
}
static unsigned long lastSensorUpdate = 0;
if (millis() - lastSensorUpdate > 3000) {
lastSensorUpdate = millis();
float sensorValue = 50.0 + 30.0 * sin(millis() / 10000.0);
currentGaugeValue = sensorValue;
webAnalogGaugePage.sendToWebAnalogGauge(currentGaugeValue);
webTablePage.sendValueUpdate("Gauge Value", String(currentGaugeValue, 1) + "%");
}
delay(10);
}
const char WIFI_SSID[] = "YOUR_WIFI_NETWORK";
const char WIFI_PASSWORD[] = "YOUR_WIFI_PASSWORD";
DIYables WebApp - Multiple Apps Example
INFO: Added app /
INFO: Added app /web-monitor
INFO: Added app /web-slider
INFO: Added app /web-joystick
INFO: Added app /web-rotator
INFO: Added app /web-gauge
INFO: Added app /web-table
DIYables WebApp Library
Platform: Arduino Uno R4 WiFi
Network connected!
IP address: 192.168.0.2
HTTP server started on port 80
Configuring WebSocket server callbacks...
WebSocket server started on port 81
WebSocket URL: ws://192.168.0.2:81
WebSocket server started on port 81
==========================================
DIYables WebApp Ready!
==========================================
📱 Web Interface: http://192.168.0.2
🔗 WebSocket: ws://192.168.0.2:81
📋 Available Applications:
🏠 Home Page: http://192.168.0.2/
📊 Web Monitor: http://192.168.0.2/web-monitor
🎚️ Web Slider: http://192.168.0.2/web-slider
🕹️ Web Joystick: http://192.168.0.2/web-joystick
🔄 Web Rotator: http://192.168.0.2/web-rotator
⏲️ Web Analog Gauge: http://192.168.0.2/web-gauge
📊 Web Table: http://192.168.0.2/web-table
==========================================
Nếu bạn không thấy gì, hãy khởi động lại bo Arduino.
Ghi nhớ địa chỉ IP hiển thị và nhập địa chỉ này vào thanh địa chỉ của trình duyệt web trên smartphone hoặc PC.
Ví dụ: http://192.168.0.2
Bạn sẽ thấy trang chủ với tất cả ứng dụng web như hình dưới đây:
Nhấp vào bất kỳ liên kết ứng dụng web nào (Chat, Web Monitor, Web Digital Pins, Web Slider, Web Joystick, v.v.), bạn sẽ thấy giao diện của ứng dụng web tương ứng.
Hoặc bạn cũng có thể truy cập trực tiếp từng trang bằng địa chỉ IP theo sau là đường dẫn ứng dụng. Ví dụ: http://192.168.0.2/chat, http://192.168.0.2/web-monitor, v.v.
Khám phá tất cả ứng dụng web: thử chat với Arduino, giám sát serial output, điều khiển digital pin, điều chỉnh slider và sử dụng joystick ảo để trải nghiệm đầy đủ khả năng của giao diện web tích hợp.
Trang chủ đóng vai trò là trung tâm điều khiển với liên kết đến tất cả ứng dụng:
Web Monitor: /webmonitor - Giao diện giao tiếp serial
Chat: /chat - Giao tiếp tương tác với Arduino
Digital Pins: /digital-pins - Điều khiển và giám sát pin
Web Slider: /webslider - Hai slider điều khiển analog
Web Joystick: /webjoystick - Giao diện điều khiển vị trí 2D
Truy cập trực tiếp từng giao diện:
http://[ARDUINO_IP]/ # Trang chủ
http://[ARDUINO_IP]/webmonitor # Giao diện serial monitor
http://[ARDUINO_IP]/chat # Giao diện chat
http://[ARDUINO_IP]/digital-pins # Điều khiển pin
http://[ARDUINO_IP]/webslider # Điều khiển slider
http://[ARDUINO_IP]/webjoystick # Điều khiển joystick
Ví dụ toàn diện này cung cấp nền tảng cho các dự án sáng tạo của bạn. Chỉnh sửa và điều chỉnh các cấu hình dưới đây để xây dựng các ứng dụng IoT tuyệt vời phù hợp với tầm nhìn độc đáo của bạn.
Ví dụ cấu hình sẵn các pin cụ thể cho các mục đích khác nhau:
webDigitalPinsPage.enablePin(2, WEB_PIN_OUTPUT);
webDigitalPinsPage.enablePin(3, WEB_PIN_OUTPUT);
webDigitalPinsPage.enablePin(4, WEB_PIN_OUTPUT);
webDigitalPinsPage.enablePin(13, WEB_PIN_OUTPUT);
webDigitalPinsPage.enablePin(8, WEB_PIN_INPUT);
webDigitalPinsPage.enablePin(9, WEB_PIN_INPUT);
DIYablesWebJoystickPage webJoystickPage(false, 5);
Ví dụ duy trì trạng thái đồng bộ trên tất cả giao diện:
int pinStates[16] = { LOW };
int currentSlider1 = 64;
int currentSlider2 = 128;
int currentJoystickX = 0;
int currentJoystickY = 0;
Giao diện chat bao gồm một số lệnh được lập trình sẵn:
hello - Phản hồi chào hỏi thân thiện
time - Hiển thị thời gian hoạt động của Arduino tính bằng giây
status - Báo cáo trạng thái Arduino và trạng thái LED
help - Liệt kê các lệnh có sẵn
User: hello
Arduino: Hello! I'm your Arduino. How can I help you?
User: led on
Arduino: Built-in LED is now ON!
User: time
Arduino: I've been running for 1245 seconds.
User: status
Arduino: Status: Running smoothly! LED is ON
#include <Servo.h>
const int MOTOR_LEFT_PWM = 9;
const int MOTOR_RIGHT_PWM = 10;
const int SERVO_PAN = 11;
const int SERVO_TILT = 12;
const int LED_STRIP_PIN = 6;
Servo panServo, tiltServo;
void setup() {
panServo.attach(SERVO_PAN);
tiltServo.attach(SERVO_TILT);
pinMode(MOTOR_LEFT_PWM, OUTPUT);
pinMode(MOTOR_RIGHT_PWM, OUTPUT);
setupRobotCallbacks();
}
void setupRobotCallbacks() {
webJoystickPage.onJoystickValueFromWeb([](int x, int y) {
int leftSpeed = y + (x / 2);
int rightSpeed = y - (x / 2);
leftSpeed = constrain(leftSpeed, -100, 100);
rightSpeed = constrain(rightSpeed, -100, 100);
leftSpeed = map(leftSpeed, -100, 100, -currentSlider1, currentSlider1);
rightSpeed = map(rightSpeed, -100, 100, -currentSlider1, currentSlider1);
analogWrite(MOTOR_LEFT_PWM, abs(leftSpeed));
analogWrite(MOTOR_RIGHT_PWM, abs(rightSpeed));
Serial.println("Robot - Trái: " + String(leftSpeed) + ", Phải: " + String(rightSpeed));
});
webSliderPage.onSliderValueFromWeb([](int slider1, int slider2) {
int panAngle = map(currentJoystickX, -100, 100, 0, 180);
int tiltAngle = map(slider2, 0, 255, 0, 180);
panServo.write(panAngle);
tiltServo.write(tiltAngle);
Serial.println("Camera - Pan: " + String(panAngle) + "°, Tilt: " + String(tiltAngle) + "°");
});
webDigitalPinsPage.onPinWrite([](int pin, int state) {
switch (pin) {
case 2:
digitalWrite(pin, state);
Serial.println("Đèn pha " + String(state ? "BẬT" : "TẮT"));
break;
case 3:
if (state) {
digitalWrite(pin, HIGH);
delay(200);
digitalWrite(pin, LOW);
}
break;
case 4:
if (state) {
analogWrite(MOTOR_LEFT_PWM, 0);
analogWrite(MOTOR_RIGHT_PWM, 0);
Serial.println("KÍCH HOẠT DỪNG KHẨN CẤP");
}
break;
}
});
chatPage.onChatMessage([](const String& message) {
String msg = message;
msg.toLowerCase();
if (msg.indexOf("stop") >= 0) {
analogWrite(MOTOR_LEFT_PWM, 0);
analogWrite(MOTOR_RIGHT_PWM, 0);
chatPage.sendToChat("Robot đã dừng!");
return;
}
if (msg.indexOf("center camera") >= 0) {
panServo.write(90);
tiltServo.write(90);
chatPage.sendToChat("Camera đã được căn giữa!");
return;
}
if (msg.indexOf("speed") >= 0) {
String response = "Tốc độ tối đa hiện tại: " + String(map(currentSlider1, 0, 255, 0, 100)) + "%";
chatPage.sendToChat(response);
return;
}
chatPage.sendToChat("Lệnh robot: stop, center camera, speed");
});
}
const int LIVING_ROOM_LIGHTS = 2;
const int BEDROOM_LIGHTS = 3;
const int KITCHEN_LIGHTS = 4;
const int FAN_CONTROL = 9;
const int AC_CONTROL = 10;
const int MOTION_SENSOR = 8;
const int DOOR_SENSOR = 9;
void setupHomeAutomation() {
pinMode(LIVING_ROOM_LIGHTS, OUTPUT);
pinMode(BEDROOM_LIGHTS, OUTPUT);
pinMode(KITCHEN_LIGHTS, OUTPUT);
pinMode(FAN_CONTROL, OUTPUT);
pinMode(AC_CONTROL, OUTPUT);
pinMode(MOTION_SENSOR, INPUT);
pinMode(DOOR_SENSOR, INPUT_PULLUP);
webDigitalPinsPage.onPinWrite([](int pin, int state) {
digitalWrite(pin, state);
String room;
switch (pin) {
case 2: room = "Phòng Khách"; break;
case 3: room = "Phòng Ngủ"; break;
case 4: room = "Nhà Bếp"; break;
default: room = "Pin " + String(pin); break;
}
Serial.println("Đèn " + room + " " + String(state ? "BẬT" : "TẮT"));
String message = "Đèn " + room + " đã " + String(state ? "BẬT" : "TẮT");
chatPage.sendToChat(message);
});
webSliderPage.onSliderValueFromWeb([](int slider1, int slider2) {
analogWrite(FAN_CONTROL, slider1);
analogWrite(AC_CONTROL, slider2);
Serial.println("Quạt: " + String(map(slider1, 0, 255, 0, 100)) + "%, " +
"AC: " + String(map(slider2, 0, 255, 0, 100)) + "%");
});
chatPage.onChatMessage([](const String& message) {
String msg = message;
msg.toLowerCase();
if (msg.indexOf("all lights on") >= 0) {
digitalWrite(LIVING_ROOM_LIGHTS, HIGH);
digitalWrite(BEDROOM_LIGHTS, HIGH);
digitalWrite(KITCHEN_LIGHTS, HIGH);
chatPage.sendToChat("Tất cả đèn đã BẬT!");
return;
}
if (msg.indexOf("all lights off") >= 0) {
digitalWrite(LIVING_ROOM_LIGHTS, LOW);
digitalWrite(BEDROOM_LIGHTS, LOW);
digitalWrite(KITCHEN_LIGHTS, LOW);
chatPage.sendToChat("Tất cả đèn đã TẮT!");
return;
}
if (msg.indexOf("temperature") >= 0) {
String response = "Quạt: " + String(map(currentSlider1, 0, 255, 0, 100)) + "%, " +
"AC: " + String(map(currentSlider2, 0, 255, 0, 100)) + "%";
chatPage.sendToChat(response);
return;
}
if (msg.indexOf("security") >= 0) {
bool motion = digitalRead(MOTION_SENSOR);
bool door = digitalRead(DOOR_SENSOR);
String status = "Cảm biến chuyển động: " + String(motion ? "PHÁT HIỆN" : "BÌNH THƯỜNG") +
", Cửa: " + String(door ? "ĐÓNG" : "MỞ");
chatPage.sendToChat(status);
return;
}
chatPage.sendToChat("Lệnh nhà: all lights on/off, temperature, security");
});
}
void loop() {
server.loop();
static bool lastMotion = false;
static bool lastDoor = false;
bool currentMotion = digitalRead(MOTION_SENSOR);
bool currentDoor = digitalRead(DOOR_SENSOR);
if (currentMotion != lastMotion) {
if (currentMotion) {
chatPage.sendToChat("🚨 PHÁT HIỆN CHUYỂN ĐỘNG!");
webMonitorPage.sendToWebMonitor("Cảnh báo An ninh: Phát hiện chuyển động");
}
lastMotion = currentMotion;
}
if (currentDoor != lastDoor) {
String status = currentDoor ? "ĐÓNG" : "MỞ";
chatPage.sendToChat("🚪 Cửa " + status);
webMonitorPage.sendToWebMonitor("An ninh: Cửa " + status);
lastDoor = currentDoor;
}
delay(10);
}