2026-09 mqtt 實驗1
MQTT (Message Queuing Telemetry Transport) 是一種輕量級的發佈/訂閱 (Publish/Subscribe) 訊息傳輸協定,專為網路頻寬有限、硬體資源受限或網路不穩定的物聯網 (IoT) 裝置與 M2M (Machine-to-Machine) 通訊而設計。
核心架構與運作機制
發佈/訂閱模式 (Pub/Sub):傳送者(發佈者)與接收者(訂閱者)不直接對連,解耦了裝置間的時間與空間依賴。
代理伺服器 (Broker):訊息的中繼站。負責接收發佈者的訊息、過濾並轉發給已訂閱該主題的客戶端(如 ESP32、Python 應用程式)。
主題 (Topic):訊息的分類標籤,採階層式路徑設計(例如
home/bedroom/temperature或alex9ufo/2026/led)。
主要特性
極輕量與高效能:標頭檔最小僅 2 Bytes,大幅降低網路封包負擔與裝置耗電量。
基於 TCP/IP:確保資料傳輸的可靠性與順序性(預設 Port 為 1883,加密的 MQTTS 使用 Port 8883)。
服務品質等級 (QoS, Quality of Service):
QoS 0 (At most once):最多一次,發後即忘(可能遺失)。
QoS 1 (At least once):至少一次,確保送達(可能重複)。
QoS 2 (Exactly once):恰好一次,確保不重覆且不遺失(傳送開銷最大)。
遺言訊息 (Last Will and Testament, LWT):當裝置異常斷線時,Broker 會自動發佈事先預設好的訊息告知其他客戶端。
保留訊息 (Retained Message):Broker 會儲存主題最後一筆訊息,新訂閱者連線時能立即取得當前最新的狀態。
典型應用場景
物聯網與智慧家庭:感測器數據採集(溫濕度、電量)與家電遠端控制(如 ESP32 LED 控制)。
車聯網與追蹤系統:車輛 GPS 定位與狀態回傳。
即時通訊:部分行動裝置 App 的即時訊息推播服務。
ESP32 程式功能說明
網路與 MQTT 連線建立:開機後自動連線至 Wokwi 虛擬 Wi-Fi (
Wokwi-GUEST),並連接至mqttgo.ioBroker (Port 1883)。連線成功後訂閱控制主題alex9ufo/2026/led。命令接收與模式切換 (Callback):收到指令後切換
currentMode狀態,並立即向alex9ufo/2026/ledstatus主題回報最新狀態:"on":將 GPIO 2 設為高電位(亮燈),回傳"on_led"。"off":將 GPIO 2 設為低電位(熄滅),回傳"off_led"。"flash":進入閃爍模式,回傳"flash_led"。"timer(5Sec)":點亮 LED 並記錄起始時間,回傳"timer(5Sec)_led"。
非阻塞式背景任務 (Loop):利用
millis()時間差處理持續性動作,避免使用delay()導致 MQTT 斷線:閃爍模式:每 500 毫秒翻轉一次 GPIO 2 電位。
定時模式:持續滿 5 秒後自動關閉 LED,將模式切回
OFF並回傳"off_led"。
Python (Tkinter) 程式功能說明
圖形化操作介面 (GUI):使用 Tkinter 打造控制面板,提供
ON、OFF、FLASH、Timer (定時5秒)四個控制按鈕。雙向 MQTT 通訊:
發送端:點擊按鈕時,將對應的指令字串(如
"on"、"flash")發送至alex9ufo/2026/led主題。接收端:訂閱
alex9ufo/2026/ledstatus主題,即時接收 ESP32 回傳的當前狀態。
狀態動態更新:
MQTT 連線狀態:監聽 MQTT 連線狀態(
on_connect/on_disconnect),在介面上以不同顏色顯示「連線中」、「已連線」或「已斷線」。LED 實時狀態:接收到 ESP32 回傳訊息時,動態更新介面上的
LED 狀態文字(例如 ESP32 定時 5 秒結束發送"off_led"時,介面也會同步更新為關閉狀態)。
背景異步執行:採用
loop_start()讓 MQTT 在獨立執行緒處理網路封包,確保 GUI 畫面順暢不卡頓。
Python+TKinter程式
import tkinter as tk
import paho.mqtt.client as mqtt
BROKER = "mqttgo.io"
PORT = 1883
TOPIC_PUB = "alex9ufo/2026/led"
TOPIC_SUB = "alex9ufo/ledstatus"
# MQTT v2 Callback: 處理連線狀態
def on_connect(client, userdata, flags, reason_code, properties=None):
if reason_code == 0:
mqtt_status_label.config(text="MQTT 連線狀態: 已連線", fg="green")
client.subscribe(TOPIC_SUB)
else:
mqtt_status_label.config(text=f"MQTT 連線失敗 (代碼: {reason_code})", fg="red")
# MQTT v2 Callback: 處理斷線狀態
def on_disconnect(client, userdata, disconnect_flags, reason_code, properties=None):
mqtt_status_label.config(text="MQTT 連線狀態: 已斷線", fg="red")
# MQTT Callback: 接收 ESP32 回傳的狀態
def on_message(client, userdata, msg):
payload = msg.payload.decode("utf-8")
led_status_label.config(text=f"LED 狀態: {payload}")
def send_cmd(cmd):
client.publish(TOPIC_PUB, cmd)
# 初始化 MQTT (使用 Callback API v2)
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
client.on_connect = on_connect
client.on_disconnect = on_disconnect
client.on_message = on_message
# GUI 介面設計
root = tk.Tk()
root.title("ESP32 LED 控制器")
root.geometry("320x300")
# 顯示 MQTT 連線狀態
mqtt_status_label = tk.Label(root, text="MQTT 連線狀態: 連線中...", font=("Arial", 10), fg="orange")
mqtt_status_label.pack(pady=5)
# 顯示 LED 目前狀態
led_status_label = tk.Label(root, text="LED 狀態: 未知", font=("Arial", 12, "bold"), fg="blue")
led_status_label.pack(pady=10)
# 控制按鈕
btn_on = tk.Button(root, text="ON (開啟)", width=20, command=lambda: send_cmd("on"))
btn_on.pack(pady=5)
btn_off = tk.Button(root, text="OFF (關閉)", width=20, command=lambda: send_cmd("off"))
btn_off.pack(pady=5)
btn_flash = tk.Button(root, text="FLASH (閃爍)", width=20, command=lambda: send_cmd("flash"))
btn_flash.pack(pady=5)
btn_timer = tk.Button(root, text="Timer (定時5秒)", width=20, command=lambda: send_cmd("timer(5Sec)"))
btn_timer.pack(pady=5)
# 非阻塞方式連接 Broker
try:
client.connect_async(BROKER, PORT, 60)
client.loop_start()
except Exception as e:
mqtt_status_label.config(text=f"連線異常: {e}", fg="red")
root.mainloop()
WOKWI ESP32 程式









沒有留言:
張貼留言