ESP32 雙核心控制 LED 與 DHT22 溫濕度感測器 (Wokwi 模擬) EX2 --Python TKinter
Tkinter 程式將作為一個桌面應用,可以讓你:
直接點擊按鈕控制 ESP32 上的 LED (開/關/閃爍/定時)。
即時顯示從 ESP32 接收到的 DHT22 溫濕度數據。
顯示 ESP32 的連線狀態。
Tkinter GUI 應用程式架構
這個 Tkinter 應用程式仍然會依賴 MQTT Broker 來與 ESP32 溝通。
Tkinter GUI 介面:
提供按鈕來發送 LED 控制指令到 MQTT Broker。
顯示文字標籤來更新溫濕度數據和設備狀態。
MQTT 客戶端 (在 Tkinter 程式內):
連接到相同的 MQTT Broker (
broker.hivemq.com)。發布 LED 控制指令到
telegram_iot/esp32/led_control主題。訂閱
telegram_iot/esp32/temperature,telegram_iot/esp32/humidity,telegram_iot/esp32/status主題,以接收來自 ESP32 的數據和狀態更新。在背景執行緒中處理 MQTT 訊息,並安全地更新 Tkinter 介面。
Python程式 (在THONNY上執行)
import tkinter as tk
from tkinter import messagebox
import paho.mqtt.client as mqtt
import threading
import time
import sys
# --- MQTT 設定 (必須與 ESP32 程式碼中的主題名稱一致) ---
MQTT_BROKER = "broker.mqttgo.io"
MQTT_PORT = 1883
MQTT_CLIENT_ID = "Tkinter_GUI_Controller_001" # 這個 GUI 程式的唯一 MQTT 客戶端 ID
# MQTT 主題 (與 ESP32 程式碼一致)
TELEGRAM_MQTT_TOPIC_LED_CONTROL = "telegram_iot/esp32/led_control"
TELEGRAM_MQTT_TOPIC_TEMPERATURE = "telegram_iot/esp32/temperature"
TELEGRAM_MQTT_TOPIC_HUMIDITY = "telegram_iot/esp32/humidity"
TELEGRAM_MQTT_TOPIC_STATUS = "telegram_iot/esp32/status"
# --- 全域變數,用於儲存最新的數據和狀態 ---
latest_temperature = "N/A"
latest_humidity = "N/A"
esp32_status = "Offline" # 初始狀態
# --- Tkinter GUI 介面設定 ---
class MqttControllerApp:
def __init__(self, master):
self.master = master
master.title("ESP32 IoT 控制面板")
master.geometry("400x300") # 設定視窗大小
# 溫濕度顯示標籤
self.temp_label = tk.Label(master, text=f"溫度: {latest_temperature}°C", font=("Arial", 14))
self.temp_label.pack(pady=5)
self.humidity_label = tk.Label(master, text=f"濕度: {latest_humidity}%", font=("Arial", 14))
self.humidity_label.pack(pady=5)
self.status_label = tk.Label(master, text=f"ESP32 狀態: {esp32_status}", font=("Arial", 12), fg="red")
self.status_label.pack(pady=5)
# LED 控制按鈕
self.led_frame = tk.LabelFrame(master, text="LED 控制", padx=10, pady=10)
self.led_frame.pack(pady=10)
self.btn_on = tk.Button(self.led_frame, text="開啟 LED", command=lambda: self.publish_led_command("on"), width=15)
self.btn_on.grid(row=0, column=0, padx=5, pady=5)
self.btn_off = tk.Button(self.led_frame, text="關閉 LED", command=lambda: self.publish_led_command("off"), width=15)
self.btn_off.grid(row=0, column=1, padx=5, pady=5)
self.btn_flash = tk.Button(self.led_frame, text="LED 閃爍", command=lambda: self.publish_led_command("flash"), width=15)
self.btn_flash.grid(row=1, column=0, padx=5, pady=5)
self.btn_timer = tk.Button(self.led_frame, text="定時關閉 (10秒)", command=lambda: self.publish_led_command("timer"), width=15)
self.btn_timer.grid(row=1, column=1, padx=5, pady=5)
# MQTT 客戶端初始化和連線
self.mqtt_client = mqtt.Client(client_id=MQTT_CLIENT_ID)
self.mqtt_client.on_connect = self._on_connect
self.mqtt_client.on_message = self._on_message
try:
self.mqtt_client.connect(MQTT_BROKER, MQTT_PORT, 60)
# 在一個單獨的執行緒中運行 MQTT 網路循環,以免阻塞 GUI
self.mqtt_thread = threading.Thread(target=self.mqtt_client.loop_forever)
self.mqtt_thread.daemon = True # 設定為守護執行緒,主程式退出時自動結束
self.mqtt_thread.start()
print("MQTT 客戶端啟動並嘗試連接 Broker...")
except Exception as e:
print(f"MQTT 連線失敗: {e}")
messagebox.showerror("MQTT 連線錯誤", f"無法連接到 MQTT Broker: {e}")
master.destroy() # 如果無法連線,直接關閉應用程式
# 設置關閉視窗時的處理函數
master.protocol("WM_DELETE_WINDOW", self.on_closing)
# 啟動定時更新 GUI 的函數
self.update_gui()
def _on_connect(self, client, userdata, flags, rc):
"""MQTT 連線成功時的回調函數"""
if rc == 0:
print("成功連接到 MQTT Broker!")
# 連線成功後,訂閱相關主題
client.subscribe(TELEGRAM_MQTT_TOPIC_TEMPERATURE)
client.subscribe(TELEGRAM_MQTT_TOPIC_HUMIDITY)
client.subscribe(TELEGRAM_MQTT_TOPIC_STATUS)
print(f"已訂閱: {TELEGRAM_MQTT_TOPIC_TEMPERATURE}, {TELEGRAM_MQTT_TOPIC_HUMIDITY}, {TELEGRAM_MQTT_TOPIC_STATUS}")
self.master.after(0, self.update_status_label, "Online", "green") # 在 GUI 主執行緒中更新狀態
else:
print(f"無法連接到 MQTT Broker, 返回碼: {rc}")
self.master.after(0, self.update_status_label, f"連線錯誤({rc})", "red")
def _on_message(self, client, userdata, msg):
"""收到 MQTT 訊息時的回調函數"""
global latest_temperature, latest_humidity, esp32_status
topic = msg.topic
payload = msg.payload.decode()
print(f"收到訊息 - 主題: '{topic}', 內容: '{payload}'")
if topic == TELEGRAM_MQTT_TOPIC_TEMPERATURE:
latest_temperature = payload
elif topic == TELEGRAM_MQTT_TOPIC_HUMIDITY:
latest_humidity = payload
elif topic == TELEGRAM_MQTT_TOPIC_STATUS:
esp32_status = payload
# 根據 ESP32 傳來的狀態更新狀態標籤顏色
if esp32_status == "ESP32_online":
self.master.after(0, self.update_status_label, "Online", "green")
else:
self.master.after(0, self.update_status_label, esp32_status, "red") # 假設其他狀態為離線或錯誤
# 收到任何數據更新時,排程更新 GUI
self.master.after(0, self.update_gui) # 使用 after() 在 GUI 主執行緒中安全地更新
def publish_led_command(self, command):
"""發布 LED 控制指令到 MQTT Broker"""
print(f"發布 LED 指令: {command}")
self.mqtt_client.publish(TELEGRAM_MQTT_TOPIC_LED_CONTROL, command)
def update_gui(self):
"""更新 Tkinter 介面上的數據顯示"""
self.temp_label.config(text=f"溫度: {latest_temperature}°C")
self.humidity_label.config(text=f"濕度: {latest_humidity}%")
# 狀態標籤的更新由 _on_message 觸發,這裡只需確保數據是最新的
# 每隔 100 毫秒再次排程更新,保持介面響應
self.master.after(100, self.update_gui)
def update_status_label(self, status_text, color):
"""更新狀態標籤的文字和顏色"""
self.status_label.config(text=f"ESP32 狀態: {status_text}", fg=color)
def on_closing(self):
"""視窗關閉時的回調函數,用於清理資源"""
print("關閉應用程式,斷開 MQTT 連線...")
self.mqtt_client.loop_stop() # 停止 MQTT 網路循環
self.mqtt_client.disconnect() # 斷開 MQTT 連線
self.master.destroy() # 銷毀 Tkinter 視窗
# --- 主程式入口 ---
if __name__ == "__main__":
root = tk.Tk() # 創建 Tkinter 根視窗
app = MqttControllerApp(root) # 實例化應用程式
root.mainloop() # 啟動 Tkinter 事件循環
觀察狀態:剛開始運行時,"ESP32 狀態" 應該是 "Offline"。當你的 ESP32 連接到 MQTT Broker 並發送 "ESP32_online" 狀態後,GUI 上的狀態會更新為 "Online",顏色也會變成綠色。
控制 LED:點擊 "開啟 LED", "關閉 LED", "LED 閃爍", "定時關閉 (10秒)" 按鈕,觀察 Wokwi 模擬器或你的實際 ESP32 上的 LED 行為。
查看溫濕度:每當 ESP32 發布新的溫濕度數據時,Tkinter 介面上的「溫度」和「濕度」標籤會自動更新。
注意事項
唯一性 ID:
MQTT_CLIENT_ID在 ESP32 程式和 Tkinter 程式中都必須是獨特的。如果你運行多個 MQTT 客戶端,它們應該有不同的 Client ID。多執行緒:Tkinter GUI 程式使用了一個單獨的執行緒 (
threading.Thread) 來運行mqtt_client.loop_forever()。這是為了確保 MQTT 訊息處理不會阻塞 Tkinter 的主事件循環,使 GUI 保持響應。安全更新 GUI:來自 MQTT 執行緒的數據更新必須透過
self.master.after(0, ...)排程到 Tkinter 的主執行緒中執行,這是 Tkinter 的最佳實踐,以避免在非 GUI 執行緒中直接修改 GUI 元件導致的錯誤。錯誤處理:程式中包含了基本的 MQTT 連線錯誤處理,如果無法連線會彈出警告並關閉程式。
這個 Tkinter 應用程式提供了一個直觀的方式來控制你的 IoT 設備並監控數據,對於桌面控制和展示非常有用。
WOKWI上 Arduino esp32程式






沒有留言:
張貼留言