Hotline/Zalo: 0919.890.938 (Mr Hơn)
Bài 3. Điều khiển thiết bị bằng giọng nói dùng chatbot xiaozhi
Trong kỷ nguyên AI và IoT phát triển mạnh mẽ, việc điều khiển thiết bị bằng giọng nói không còn xa lạ nữa. Các nền tảng như Google Assistant, Alexa hay ChatGPT Voice đã giúp con người tương tác với máy móc bằng ngôn ngữ tự nhiên. Bài viết này Điện thông minh E-smart sẽ hướng dẫn bạn cách sử dụng Chatbot Xiaozhi để điều khiển thiết bị IoT bằng giọng nói — biến ESP32 trở thành một “thiết bị biết nghe lời”.
Xiaozhi là một trợ lý AI thông minh, hỗ trợ tiếng Việt, có khả năng nghe – hiểu – phản hồi giống như một người thật.
Điểm mạnh của Xiaozhi là khả năng kết nối trực tiếp với thiết bị IoT thông qua các giao thức giao tiếp như MCP hoặc API WebSocket.
Nhờ vậy, khi bạn nói:
“Xiaozhi, bật đèn phòng khách.”
→ Xiaozhi sẽ suy luận, hiểu yêu cầu, và gửi lệnh xuống ESP32 để thực thi ngay lập tức.
Điều này giúp bạn xây dựng những thiết bị điều khiển bằng giọng nói một cách nhanh chóng và hiệu quả.

Cơ chế điều khiển thiết bị bằng giọng nói
Quy trình điều khiển bằng giọng nói diễn ra theo 3 bước:
Bước 1 – Người dùng ra lệnh
Ví dụ:
“Xiaozhi, tắt đèn LED đi.”
Bước 2 – Xiaozhi phân tích và tạo lệnh điều khiển
Xiaozhi nhận diện nội dung yêu cầu → phân tích ý nghĩa → gửi lệnh JSON đến ESP32.
Bước 3 – ESP32 thực thi
ESP32 bật/tắt thiết bị theo yêu cầu và gửi phản hồi cho chatbot.
→ Người dùng giao tiếp bằng ngôn ngữ tự nhiên, thiết bị vẫn hoạt động chính xác như lập trình.

Chuẩn bị trước khi lập trình
Phần cứng
- ESP32 DevKit V1
- Cáp USB
- (Tùy chọn) Đèn LED rời hoặc relay
- Mạch chatbot xiaozhi đang hoạt động
Phần mềm
- Arduino IDE mới nhất
- Đã thêm board ESP32
- Thư viện hỗ trợ Xiaozhi (xiaozhi-mcp hoặc WebSocketMCP)
- Một tài khoản Xiaozhi tại: xiaozhi.me
Thiết lập ESP32 kết nối với Xiaozhi
Để ESP32 có thể nhận lệnh từ Xiaozhi, bạn cần lấy MCP Endpoint từ website:
- Truy cập xiaozhi.me/console/agents
- Chọn agent muốn cấu hình
- Vào mục Configure Role → MCP Settings
- Nhấn Get MCP Endpoint → Copy


Endpoint có dạng:
wss://api.xiaozhi.me/mcp/?token=xxxxESP32 sẽ kết nối WebSocket đến địa chỉ này.
Lập trình điều khiển nhiều thiết bị
Ở phần này, chúng ta sẽ cài đặt ESP32 để điều khiển 4 thiết bị thông qua chatbot Xiaozhi:
- Quạt (Fan)
- Đèn phòng khách
- Đèn phòng ngủ
- Đèn phòng bếp
- Và đặc biệt: Điều khiển toàn bộ thiết bị cùng lúc
Chúng ta sẽ sử dụng WebSocket MCP – giao thức giúp Xiaozhi gửi lệnh xuống ESP32 dưới dạng JSON.
Dưới đây là toàn bộ code hoàn chỉnh và đúng cấu trúc MCP.
#include <WiFi.h>
#include <WebSocketMCP.h>
#define FAN 2
#define LIGHT1 4
#define LIGHT2 18
#define LIGHT3 19
// Cấu hình WiFi
const char* ssid = "your-ssid"; // Thay bằng tên Wi-Fi của bạn
const char* password = "your-password"; // Thay bằng mật khẩu Wi-Fi
// Cấu hình MCP Server (lấy từ chatbot Xiaozhi)
const char* mcpEndpoint = "wss://api.xiaozhi.me/mcp/?token=xxxx";
// Tạo đối tượng MCP client
WebSocketMCP mcpClient;
// Hàm callback khi kết nối/thất bại
void onConnectionStatus(bool connected) {
if (connected) {
Serial.println("[MCP] ✅ Đã kết nối tới máy chủ");
registerMcpTools();
} else {
Serial.println("[MCP] ⚠️ Mất kết nối với máy chủ MCP");
}
}
// Hàm đăng ký MCP Tools
void registerMcpTools() {
// Tool điều khiển quạt
mcpClient.registerTool(
"fan_control",
"Điều khiển quạt",
R"({
"type": "object",
"properties": {
"state": {
"type": "string",
"enum": ["on", "off"]
}
},
"required": ["state"]
})",
[](const String& args) {
DynamicJsonDocument doc(256);
deserializeJson(doc, args);
String state = doc["state"].as<String>();
if (state == "on") digitalWrite(FAN, HIGH);
else if (state == "off") digitalWrite(FAN, LOW);
return WebSocketMCP::ToolResponse("{\"success\":true,\"state\":\"" + state + "\"}");
}
);
Serial.println("[MCP] 🛠️ Đã đăng ký tool điều khiển Quạt");
// Tool điều khiển đèn phòng khách
mcpClient.registerTool(
"living_room_lights_control",
"Điều khiển đèn phòng khách",
R"({
"type": "object",
"properties": {
"state": {
"type": "string",
"enum": ["on", "off"]
}
},
"required": ["state"]
})",
[](const String& args) {
DynamicJsonDocument doc(256);
deserializeJson(doc, args);
String state = doc["state"].as<String>();
if (state == "on") digitalWrite(LIGHT1, HIGH);
else if (state == "off") digitalWrite(LIGHT1, LOW);
return WebSocketMCP::ToolResponse("{\"success\":true,\"state\":\"" + state + "\"}");
}
);
Serial.println("[MCP] 🛠️ Đã đăng ký tool điều khiển Đèn phòng khách");
// Tool điều khiển đèn phòng ngủ
mcpClient.registerTool(
"bedroom_lights_control",
"Điều khiển đèn phòng ngủ",
R"({
"type": "object",
"properties": {
"state": {
"type": "string",
"enum": ["on", "off"]
}
},
"required": ["state"]
})",
[](const String& args) {
DynamicJsonDocument doc(256);
deserializeJson(doc, args);
String state = doc["state"].as<String>();
if (state == "on") digitalWrite(LIGHT2, HIGH);
else if (state == "off") digitalWrite(LIGHT2, LOW);
return WebSocketMCP::ToolResponse("{\"success\":true,\"state\":\"" + state + "\"}");
}
);
Serial.println("[MCP] 🛠️ Đã đăng ký tool điều khiển Đèn phòng ngủ");
// Tool điều khiển đèn phòng bếp
mcpClient.registerTool(
"kitchen_lights_control",
"Điều khiển đèn phòng bếp",
R"({
"type": "object",
"properties": {
"state": {
"type": "string",
"enum": ["on", "off"]
}
},
"required": ["state"]
})",
[](const String& args) {
DynamicJsonDocument doc(256);
deserializeJson(doc, args);
String state = doc["state"].as<String>();
if (state == "on") digitalWrite(LIGHT3, HIGH);
else if (state == "off") digitalWrite(LIGHT3, LOW);
return WebSocketMCP::ToolResponse("{\"success\":true,\"state\":\"" + state + "\"}");
}
);
Serial.println("[MCP] 🛠️ Đã đăng ký tool điều khiển Đèn phòng bếp");
// Tool điều khiển tất cả thiết bị
mcpClient.registerTool(
"all_devices_control",
"Điều khiển tất cả các thiết bị",
R"({
"type": "object",
"properties": {
"state": {
"type": "string",
"enum": ["on", "off"]
}
},
"required": ["state"]
})",
[](const String& args) {
DynamicJsonDocument doc(256);
deserializeJson(doc, args);
String state = doc["state"].as<String>();
if (state == "on") {
digitalWrite(FAN, HIGH);
digitalWrite(LIGHT1, HIGH);
digitalWrite(LIGHT2, HIGH);
digitalWrite(LIGHT3, HIGH);
} else if (state == "off") {
digitalWrite(FAN, LOW);
digitalWrite(LIGHT1, LOW);
digitalWrite(LIGHT2, LOW);
digitalWrite(LIGHT3, LOW);
}
return WebSocketMCP::ToolResponse("{\"success\":true,\"state\":\"" + state + "\"}");
}
);
Serial.println("[MCP] 🛠️ Đã đăng ký tool điều khiển tất cả thiết bị");
}
void setup() {
Serial.begin(115200);
pinMode(FAN, OUTPUT);
pinMode(LIGHT1, OUTPUT);
pinMode(LIGHT2, OUTPUT);
pinMode(LIGHT3, OUTPUT);
digitalWrite(FAN, LOW);
digitalWrite(LIGHT1, LOW);
digitalWrite(LIGHT2, LOW);
digitalWrite(LIGHT3, LOW);
// Kết nối Wi-Fi
Serial.print("Đang kết nối Wi-Fi: ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\n✅ Wi-Fi đã kết nối");
Serial.println("IP thiết bị: " + WiFi.localIP().toString());
// Bắt đầu MCP
mcpClient.begin(mcpEndpoint, onConnectionStatus);
}
void loop() {
mcpClient.loop();
delay(10);
}
Cấu trúc phần cứng
Mỗi thiết bị được gán vào một chân GPIO:
| Thiết bị | Chân GPIO |
|---|---|
| Quạt | 2 |
| Đèn phòng khách | 4 |
| Đèn phòng ngủ | 18 |
| Đèn phòng bếp | 19 |

Chúng ta sẽ bật/tắt từng thiết bị bằng lệnh giọng nói thông qua Xiaozhi → MCP → ESP32.
Các MCP Tool đã đăng ký
Trong code, mình đã tạo 5 tool điều khiển, mỗi tool có nhiệm vụ riêng:
| Tên Tool | Chức năng |
|---|---|
fan_control | Bật/tắt quạt |
living_room_lights_control | Bật/tắt đèn phòng khách |
bedroom_lights_control | Bật/tắt đèn phòng ngủ |
kitchen_lights_control | Bật/tắt đèn phòng bếp |
all_devices_control | Điều khiển tất cả thiết bị đồng thời |

Khi người dùng nói:
“Xiaozhi, bật đèn phòng khách.”
“Xiaozhi, tắt quạt giúp tôi.”
“Xiaozhi, tắt toàn bộ thiết bị.”
→ Chatbot sẽ phân tích câu nói, chọn đúng MCP tool, và gửi lệnh JSON như:
{"state": "on"} hoặc {"state": "off"}ESP32 nhận lệnh → xử lý trong hàm callback → bật/tắt GPIO tương ứng.
Cách mỗi tool hoạt động
Mỗi MCP tool đều có 3 phần chính:
1️⃣ Tên tool (vd: “fan_control”)
Đây là tên Xiaozhi sẽ gọi trực tiếp khi cần điều khiển.
2️⃣ JSON Schema xác định tham số
Ví dụ:
{
"type": "object",
"properties": {
"state": {
"type": "string",
"enum": ["on", "off"]
}
},
"required": ["state"]
}Giúp Xiaozhi biết lệnh cần gửi xuống gồm gì.
3️⃣ Callback thực thi lệnh
Ví dụ tool quạt:
if (state == "on") digitalWrite(FAN, HIGH);
if (state == "off") digitalWrite(FAN, LOW);Tool nào cũng trả về phản hồi JSON:
{"success": true, "state": "on"}
Giúp Xiaozhi đọc kết quả và trả lời người dùng:
“Đã bật quạt.”
Điều khiển tất cả thiết bị cùng lúc
Tool all_devices_control cực kỳ hữu ích:
if (state == "on") {
digitalWrite(FAN, HIGH);
digitalWrite(LIGHT1, HIGH);
digitalWrite(LIGHT2, HIGH);
digitalWrite(LIGHT3, HIGH);
} else if (state == "off") {
digitalWrite(FAN, LOW);
digitalWrite(LIGHT1, LOW);
digitalWrite(LIGHT2, LOW);
digitalWrite(LIGHT3, LOW);
}Nó hỗ trợ các câu:
“Bật hết tất cả thiết bị.”
“Tắt toàn bộ đèn và quạt.”
Trong nhà thông minh, đây là lệnh “Good night” hoặc “Good morning”.
Cách hệ thống hoạt động thực tế
Toàn bộ quy trình diễn ra như sau:
Bước 1 – Người dùng nói
“Xiaozhi, bật đèn phòng ngủ.”
Bước 2 – Xiaozhi phân tích câu nói
→ Nhận ra đây là yêu cầu điều khiển đèn phòng ngủ
→ Lựa chọn tool: bedroom_lights_control
→ Gửi gói JSON xuống ESP32:
{"state":"on"}Bước 3 – ESP32 xử lý
- Nhận lệnh qua WebSocket
- Callback bật GPIO 18
- Trả kết quả cho Xiaozhi
Bước 4 – Xiaozhi phản hồi lại người dùng
“Đã bật đèn phòng ngủ cho bạn.”
Kết luận
Với MCP và Chatbot Xiaozhi, ESP32 có thể biến thành một hệ thống AI Home Assistant thực sự, có khả năng hiểu tiếng Việt tự nhiên, điều khiển đồng thời nhiều thiết bị, phản hồi theo thời gian thực và mở rộng một cách cực kỳ đơn giản.
Đây là bước nền tảng tuyệt vời để bạn phát triển tiếp các giải pháp như tự động hóa nhà thông minh, phòng ngủ thông minh, phòng khách AI, hoặc một trợ lý điều khiển bằng giọng nói.







