2025-09 上學期<作業1> MQTT + WOKWI ESP32 (DHT22,LED) + MQTTX + MQTTGO.io + Python TKInter
作業1,2 每位同學都要繳交
作業3,4 以組為單位1至3人
作業執行結果上傳到YT上 影片超連結 mail 到 alex9ufo@gmail.com
(分數 依照繳交的先後次序)
執行後結果 https://www.youtube.com/watch?v=FY32EG7rwMc
# MQTT 設定
BROKER_HOST = "broker.mqttgo.io" PORT = 1883
LED_TOPIC = "wokwi/led/control"
TEMP_TOPIC = "wokwi/dht/temperature"
HUMIDITY_TOPIC = "wokwi/dht/humidity"
WOKWI ESP32
https://broker.mqttgo.io/的設定 (Broker + 推播 Publish + 訂閱 Subscriptions ) 要先連線再設定
MQTTX設定 (載點 https://mqttx.app/downloads )
PYTHON + TKInter
下載 https://thonny.org/ <<python>>
安裝套件
使用 Python Tkinter 庫的 MQTT 應用程式。這個程式將具備以下功能:
連接到 MQTT Broker (broker.mqttgo.io)。
提供使用者介面來發送指令,控制 LED 燈(開啟、關閉、閃爍、定時)。
訂閱並顯示來自兩個不同主題的溫度和濕度資料。
所有這些功能都將整合在一個單一的 Tkinter 視窗中。
import tkinter as tk
from tkinter import ttk
import paho.mqtt.client as mqtt
import threading
import json
# MQTT 設定
BROKER_HOST = "broker.mqttgo.io"
PORT = 1883
LED_TOPIC = "wokwi/led/control"
TEMP_TOPIC = "wokwi/dht/temperature"
HUMIDITY_TOPIC = "wokwi/dht/humidity"
class MqttApp(tk.Tk):
def __init__(self):
super().__init__()
self.title("MQTT LED and Sensor Monitor")
self.geometry("400x300")
self.resizable(False, False)
self.mqtt_client = mqtt.Client(protocol=mqtt.MQTTv311)
self.mqtt_client.on_connect = self.on_connect
self.mqtt_client.on_message = self.on_message
self.setup_ui()
self.connect_mqtt()
def setup_ui(self):
# 建立框架
main_frame = ttk.Frame(self, padding="10")
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
# LED 控制區
led_frame = ttk.LabelFrame(main_frame, text="LED Control", padding="10")
led_frame.grid(row=0, column=0, padx=5, pady=5, sticky="ew")
ttk.Button(led_frame, text="ON", command=lambda: self.publish_led_command("on")).pack(fill="x", pady=2)
ttk.Button(led_frame, text="OFF", command=lambda: self.publish_led_command("off")).pack(fill="x", pady=2)
ttk.Button(led_frame, text="FLASH", command=lambda: self.publish_led_command("flash")).pack(fill="x", pady=2)
ttk.Button(led_frame, text="TIMER", command=lambda: self.publish_led_command("timer")).pack(fill="x", pady=2)
# 感應器數據顯示區
sensor_frame = ttk.LabelFrame(main_frame, text="Sensor Data", padding="10")
sensor_frame.grid(row=1, column=0, padx=5, pady=10, sticky="ew")
ttk.Label(sensor_frame, text="Temperature:").grid(row=0, column=0, sticky="w", pady=2)
self.temp_label = ttk.Label(sensor_frame, text="-- °C")
self.temp_label.grid(row=0, column=1, sticky="w", padx=5)
ttk.Label(sensor_frame, text="Humidity:").grid(row=1, column=0, sticky="w", pady=2)
self.humidity_label = ttk.Label(sensor_frame, text="-- %")
self.humidity_label.grid(row=1, column=1, sticky="w", padx=5)
# 狀態顯示區
self.status_label = ttk.Label(main_frame, text="Status: Disconnected", foreground="red")
self.status_label.grid(row=2, column=0, pady=10)
def connect_mqtt(self):
try:
self.mqtt_client.connect(BROKER_HOST, PORT)
# 啟動一個新線程來處理 MQTT 網路迴圈
mqtt_thread = threading.Thread(target=self.mqtt_loop, daemon=True)
mqtt_thread.start()
except Exception as e:
self.status_label.config(text=f"Status: Connection failed. {e}", foreground="red")
def mqtt_loop(self):
self.mqtt_client.loop_forever()
def on_connect(self, client, userdata, flags, rc):
if rc == 0:
print("Connected to MQTT Broker!")
self.status_label.config(text="Status: Connected", foreground="green")
# 連接成功後訂閱主題
self.mqtt_client.subscribe(TEMP_TOPIC)
self.mqtt_client.subscribe(HUMIDITY_TOPIC)
else:
print(f"Failed to connect, return code {rc}\n")
self.status_label.config(text="Status: Connection failed", foreground="red")
def on_message(self, client, userdata, msg):
topic = msg.topic
payload = msg.payload.decode()
print(f"Received message on topic: {topic} with payload: {payload}")
if topic == TEMP_TOPIC:
self.update_temperature(payload)
elif topic == HUMIDITY_TOPIC:
self.update_humidity(payload)
def update_temperature(self, temp_str):
try:
temp_value = float(temp_str)
self.temp_label.config(text=f"{temp_value:.1f} °C")
except ValueError:
self.temp_label.config(text="Invalid data")
def update_humidity(self, humidity_str):
try:
humidity_value = float(humidity_str)
self.humidity_label.config(text=f"{humidity_value:.1f} %")
except ValueError:
self.humidity_label.config(text="Invalid data")
def publish_led_command(self, command):
try:
self.mqtt_client.publish(LED_TOPIC, command)
print(f"Published command: '{command}' to topic '{LED_TOPIC}'")
except Exception as e:
print(f"Failed to publish message: {e}")
if __name__ == "__main__":
app = MqttApp()
app.mainloop()
程式碼說明
MqttApp類別:這個類別繼承自tk.Tk,是整個應用程式的主視窗。__init__方法:初始化視窗、設定標題和大小,並創建 MQTT 客戶端實例。接著,它呼叫setup_ui建立使用者介面,並呼叫connect_mqtt連接到 MQTT Broker。setup_ui方法:這個方法負責建立所有 Tkinter 元件,包括按鈕和標籤,並使用ttk模組來獲得更現代的外觀。connect_mqtt方法:嘗試連接到指定的 MQTT Broker。為了確保 GUI 不會被 MQTT 的網路迴圈阻塞,它使用了一個新的**線程(threading)**來執行mqtt_client.loop_forever()。on_connect方法:這是 MQTT 連接成功後的回呼函數。它會將狀態標籤更新為 "Connected",並立即訂閱溫度和濕度主題。on_message方法:這是當收到訊息時觸發的回呼函數。它檢查訊息的主題,並根據不同的主題將數據傳遞給update_temperature或update_humidity方法來更新介面上的標籤。publish_led_command方法:當您點擊 LED 控制按鈕時,這個方法會將對應的命令(on,off,flash,timer)發佈到wokwi/led/control主題。update_temperature和update_humidity方法:這些方法負責解析收到的字串數據,將其轉換為浮點數,並更新介面上的文字顯示。





















沒有留言:
張貼留言