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:

  1. Truy cập xiaozhi.me/console/agents
  2. Chọn agent muốn cấu hình
  3. Vào mục Configure Role → MCP Settings
  4. Nhấn Get MCP Endpoint → Copy
Điều khiển thiết bị bằng chatbot xiaozhi
Điều khiển thiết bị bằng chatbot xiaozhi

Endpoint có dạng:

wss://api.xiaozhi.me/mcp/?token=xxxx

ESP32 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ạt2
Đèn phòng khách4
Đèn phòng ngủ18
Đèn phòng bếp19
Điều khiển thiết bị bằng giọng nói dùng chatbot xiaozhi

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 ToolChức năng
fan_controlBật/tắt quạt
living_room_lights_controlBật/tắt đèn phòng khách
bedroom_lights_controlBật/tắt đèn phòng ngủ
kitchen_lights_controlBật/tắt đèn phòng bếp
all_devices_controlĐiều khiển tất cả thiết bị đồng thời
Điều khiển thiết bị bằng chatbot xiaozhi

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.

Đánh giá bài viết

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *

Tuyển sinh khóa Lập trình ESP32 căn bản K20, lớp 2-4-6 khai giảng ngày 05/12/2025. Học phí 1tr/khóa (12 buổi). Đăng ký qua zalo: 0919.890.938 (còn 15 suất)

X
Contact Me on Zalo