Hướng dẫn chi tiết
Bài 5: Tích hợp MQTT, chạy hệ thống hoàn chỉnh và cài systemd service
Viết mqtt_client.py, tích hợp main.py hoàn chỉnh, test với ESP32 subscriber, cài systemd service để tự khởi động khi boot.
Cập nhật 05/03/2026
mqtt_client.py
import json, time
import paho.mqtt.client as mqtt
from config import MQTT_BROKER, MQTT_PORT, MQTT_TOPIC_PREFIX
_client: mqtt.Client | None = None
def connect() -> None:
global _client
_client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, "voice-assistant-rpi")
_client.on_connect = lambda c,u,f,rc,p: print(f"[MQTT] Kết nối: {rc}")
_client.connect(MQTT_BROKER, MQTT_PORT, keepalive=60)
_client.loop_start()
def publish_command(device: str, state: bool) -> None:
if not _client: return
topic = f"{MQTT_TOPIC_PREFIX}/{device}"
payload = json.dumps({"state":"ON" if state else "OFF",
"source":"voice","ts":time.time()})
_client.publish(topic, payload, qos=1)
print(f"[MQTT] → {topic}: {payload}")
def disconnect() -> None:
if _client:
_client.loop_stop(); _client.disconnect()
main.py — Tích hợp hoàn chỉnh
#!/usr/bin/env python3
import signal, sys
import gpio_controller, mqtt_client
from command_parser import parse_command
from listener import listen
from tts import announce_command_result, announce_error, speak
def handle_command(text: str) -> None:
cmd = parse_command(text)
if cmd is None:
announce_error("không hiểu lệnh này"); return
state = cmd.action == "on"
if gpio_controller.set_device(cmd.device, state):
mqtt_client.publish_command(cmd.device, state)
announce_command_result(cmd.device, state)
else:
announce_error("gặp lỗi phần cứng")
def shutdown(sig, frame):
speak("Đang tắt hệ thống.")
mqtt_client.disconnect(); gpio_controller.cleanup(); sys.exit(0)
def main():
gpio_controller.init(); mqtt_client.connect()
signal.signal(signal.SIGINT, shutdown)
signal.signal(signal.SIGTERM, shutdown)
speak("Voice Assistant đã sẵn sàng.")
listen(handle_command)
if __name__ == "__main__":
main()
ESP32 Subscriber (Arduino)
#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
WiFiClient wifiClient;
PubSubClient mqtt(wifiClient);
#define RELAY_LIGHT 26
void callback(char* topic, byte* payload, unsigned int length) {
StaticJsonDocument<128> doc;
deserializeJson(doc, payload, length);
bool on = strcmp(doc["state"] | "OFF", "ON") == 0;
if (String(topic) == "home/voice/light")
digitalWrite(RELAY_LIGHT, on ? LOW : HIGH);
}
void setup() {
pinMode(RELAY_LIGHT, OUTPUT); digitalWrite(RELAY_LIGHT, HIGH);
WiFi.begin("SSID", "PASSWORD");
while (WiFi.status() != WL_CONNECTED) delay(500);
mqtt.setServer("192.168.1.100", 1883);
mqtt.setCallback(callback);
mqtt.connect("esp32-voice-sub");
mqtt.subscribe("home/voice/#");
}
void loop() {
if (!mqtt.connected()) { mqtt.connect("esp32-voice-sub"); mqtt.subscribe("home/voice/#"); }
mqtt.loop();
}
Cài systemd service
sudo tee /etc/systemd/system/voice-assistant.service << 'EOF'
[Unit]
Description=Voice Assistant offline tieng Viet
After=network.target sound.target
[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/voice-assistant
ExecStart=/home/pi/voice-assistant/.venv/bin/python main.py
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable voice-assistant
sudo systemctl start voice-assistant
journalctl -u voice-assistant -f
Test toàn bộ hệ thống
# Terminal 1: theo dõi MQTT
mosquitto_sub -h localhost -t "home/voice/#" -v
# Terminal 2: chạy Voice Assistant
cd ~/voice-assistant && source .venv/bin/activate && python main.py
Nói "bật đèn" → Terminal 1 hiển thị: home/voice/light {"state": "ON", "source": "voice"}