Python 人機介面 設定Wifi ,MQTT 參數 控制 自訂的GPIO ON,OFF,FLASH,TIMER(20S)
當 ESP32 卡住時 (例如當前的 DNS 錯誤,或 Wi-Fi 連線失敗):
使用者操作:
將 ESP32 斷電。
將 GPIO34 = GND (或您定義的
SAFE_MODE_PIN按鈕)。再重新插電啟動 設備會進入 訂定模式 alex9ufo ,alex9981
ESP32 動作:
check_safe_mode()偵測到按鈕按下。執行
clear_config()清除 NVS 中錯誤的配置。設備使用硬編碼的預設配置(SSID: alex9ufo, Broker: broker.mqtt-dashboard.com)啟動。
恢復連線:
設備現在會連線到預設的 Wi-Fi 和 MQTT Broker (如果預設的 Wi-Fi 是可用的)。
設備重新訂閱
TOPIC_CONFIG(alex9ufo/rfid/config)。
重新配置:
在 Python GUI 中輸入正確的配置 (例如,將 Broker 從錯誤的
broker.mqttgo.io改回正確的broker.mqtt-dashboard.com)。點擊 "發送配置並儲存" 按鈕。
設備恢復:設備收到正確的配置後,會儲存 NVS 並重新啟動,從而恢復正常操作。
Python TKinter程式
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
import paho.mqtt.client as mqtt
import time
import json
import threading
# --- MQTT 設定 (用於程式自身的連線) ---
MQTT_BROKER_DEFAULT = "broker.mqtt-dashboard.com"
MQTT_PORT = 1883
# 用於傳送配置的主題,程式本身和ESP32都需要硬編碼此主題
TOPIC_CONFIG = "alex9ufo/rfid/config"
TOPIC_LED_CONTROL_DEFAULT = "alex9ufo/rfid/led"
TOPIC_LED_STATUS_DEFAULT = "alex9ufo/rfid/ledStatus"
class LedControllerApp:
def __init__(self, master):
self.master = master
master.title("MQTT LED 控制器與設定 (NVS)")
self.led_status_var = tk.StringVar(value="狀態: 離線")
self.timer_label_var = tk.StringVar(value="計時器: 閒置")
self.timer_end_time = 0
# --- 配置輸入變數 (必須初始化) ---
self.led_pin_var = tk.StringVar(value="2")
self.ssid_var = tk.StringVar(value="alex9ufo")
self.password_var = tk.StringVar(value="alex9981")
self.broker_var = tk.StringVar(value=MQTT_BROKER_DEFAULT)
self.control_topic_var = tk.StringVar(value=TOPIC_LED_CONTROL_DEFAULT)
self.status_topic_var = tk.StringVar(value=TOPIC_LED_STATUS_DEFAULT)
# --- 設定 MQTT 客戶端 ---
self.client = mqtt.Client()
self.client.on_connect = self.on_connect
self.client.on_message = self.on_message
self.connect_mqtt()
# --- 建立 GUI ---
self.create_widgets()
self.master.after(100, self.update_timer)
def connect_mqtt(self):
try:
print(f"嘗試連線到 MQTT Broker: {MQTT_BROKER_DEFAULT}")
self.client.connect(MQTT_BROKER_DEFAULT, MQTT_PORT, 60)
self.client.loop_start()
except Exception as e:
print(f"MQTT 連線失敗: {e}")
self.master.after(0, self.led_status_var.set, "狀態: 連線失敗")
def on_connect(self, client, userdata, flags, rc):
"""在 MQTT 客戶端執行緒中執行"""
if rc == 0:
print("MQTT 連線成功!")
# 訂閱狀態主題
client.subscribe(self.status_topic_var.get())
client.subscribe(TOPIC_CONFIG)
# 使用 after 轉移到主執行緒更新 UI
self.master.after(0, self.update_status_on_connect, rc)
else:
print(f"連線失敗,返回碼 {rc}")
self.master.after(0, self.update_status_on_connect, rc)
# 在主執行緒中執行 UI 更新
def update_status_on_connect(self, rc):
if rc == 0:
self.led_status_var.set("狀態: 已連線, LED未知")
else:
self.led_status_var.set(f"狀態: 連線失敗 {rc}")
def on_message(self, client, userdata, msg):
"""在 MQTT 客戶端執行緒中執行"""
topic = msg.topic
payload = msg.payload.decode()
if topic == self.status_topic_var.get():
self.master.after(0, self.update_status_on_message, payload)
if topic == TOPIC_CONFIG:
print("收到配置訊息,通常表示設備準備好接收新配置。")
# 在主執行緒中執行 UI 更新
def update_status_on_message(self, payload):
self.led_status_var.set(f"狀態: LED {payload.upper()}")
def send_command(self, command):
"""發送 LED 控制指令到 MQTT"""
if self.client.is_connected():
control_topic = self.control_topic_var.get()
self.client.publish(control_topic, command, qos=1)
print(f"發送指令到 {control_topic}: {command}")
if command == "timer_20":
self.timer_end_time = time.time() + 20
self.timer_label_var.set("計時器: 20秒倒數中...")
else:
self.timer_end_time = 0
self.timer_label_var.set("計時器: 閒置")
else:
messagebox.showerror("錯誤", "MQTT 未連線,請檢查網路連線。")
def send_config(self):
"""將所有配置參數打包成 JSON 並透過 MQTT 發送"""
try:
config_data = {
"LED_PIN": int(self.led_pin_var.get()),
"ssid": self.ssid_var.get(),
"password": self.password_var.get(),
"broker": self.broker_var.get(),
"control_topic": self.control_topic_var.get(),
"status_topic": self.status_topic_var.get()
}
except ValueError:
messagebox.showerror("錯誤", "LED PIN 必須是有效的整數。")
return
config_json = json.dumps(config_data)
if self.client.is_connected():
self.client.publish(TOPIC_CONFIG, config_json, qos=1)
print(f"發送配置數據到 {TOPIC_CONFIG}: {config_json}")
messagebox.showinfo("配置發送", "配置已發送給 ESP32。設備將儲存並重新啟動以應用新設定。\n\n**提示: 如果配置錯誤,請在啟動時按住 BOOT 按鈕進入安全模式。**")
else:
messagebox.showerror("錯誤", "MQTT 未連線,無法發送配置。")
def update_timer(self):
"""更新計時器顯示 (在主執行緒中執行)"""
if self.timer_end_time > 0:
remaining = int(self.timer_end_time - time.time())
if remaining > 0:
self.timer_label_var.set(f"計時器: 剩餘 {remaining} 秒")
else:
self.timer_end_time = 0
self.timer_label_var.set("計時器: 結束 (LED OFF)")
self.master.after(1000, self.update_timer)
def create_widgets(self):
# --- 狀態顯示區 ---
status_frame = ttk.LabelFrame(self.master, text="當前連線狀態")
status_frame.pack(pady=10, padx=10, fill="x")
ttk.Label(status_frame, textvariable=self.led_status_var, font=('Arial', 12, 'bold')).pack(pady=5)
ttk.Label(status_frame, textvariable=self.timer_label_var, font=('Arial', 10)).pack(pady=5)
# --- LED 控制區 ---
control_frame = ttk.LabelFrame(self.master, text="LED 控制")
control_frame.pack(pady=10, padx=10, fill="x")
button_frame = ttk.Frame(control_frame)
button_frame.pack(pady=10, padx=5)
ttk.Button(button_frame, text="LED ON", command=lambda: self.send_command("on")).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="LED OFF", command=lambda: self.send_command("off")).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="LED FLASH", command=lambda: self.send_command("flash")).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="TIMER (20s)", command=lambda: self.send_command("timer_20")).pack(side=tk.LEFT, padx=5)
# --- 裝置配置區 ---
config_frame = ttk.LabelFrame(self.master, text="ESP32 裝置配置 (發送並儲存到 NVS)")
config_frame.pack(pady=10, padx=10, fill="x")
fields = [
("LED PIN (GPIO)", self.led_pin_var),
("Wi-Fi SSID", self.ssid_var),
("Wi-Fi Password", self.password_var),
("MQTT Broker", self.broker_var),
("Control Topic", self.control_topic_var),
("Status Topic", self.status_topic_var)
]
for i, (label_text, variable) in enumerate(fields):
row = ttk.Frame(config_frame)
row.pack(side=tk.TOP, fill=tk.X, padx=5, pady=2)
ttk.Label(row, text=label_text + ":", width=15, anchor='w').pack(side=tk.LEFT)
ttk.Entry(row, textvariable=variable).pack(side=tk.LEFT, expand=tk.YES, fill=tk.X)
ttk.Button(config_frame, text="🚀 發送配置並儲存", command=self.send_config, style='Accent.TButton').pack(pady=10)
def on_closing(self):
"""關閉應用程式時清理資源"""
print("關閉應用程式...")
self.client.loop_stop()
self.client.disconnect()
self.master.destroy()
if __name__ == "__main__":
if not threading.main_thread().is_alive():
print("警告: Tkinter 應在主執行緒中運行。")
root = tk.Tk()
style = ttk.Style()
style.theme_use('clam')
style.configure('Accent.TButton', font=('Arial', 10, 'bold'), foreground='black', background='#4CAF50')
app = LedControllerApp(root)
root.protocol("WM_DELETE_WINDOW", app.on_closing)
root.mainloop()
GPIO34 =3.3V 使用新的訂定值
rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:1184
load:0x40078000,len:13232
load:0x40080400,len:3028
entry 0x400805e4
從 NVS 載入配置...
當前配置 LED PIN: 2
當前配置 SSID: ASUS_30
當前配置 Broker: broker.mqtt-dashboard.com
嘗試連接到 SSID: ASUS_30
.
WiFi 連線成功
IP: 192.168.50.24
嘗試 MQTT 連線到 broker.mqtt-dashboard.com...成功
GPIO34 =GND 內定值的設定
Leaving...
Hard resetting via RTS pin...
--- Terminal on COM6 | 115200 8-N-1
--- Available filters and text transformations: colorize, debug, default, direct, esp32_exception_decoder, hexlify, log2file, nocontrol, printable, send_on_enter, time
--- More details at https://bit.ly/pio-monitor-filters
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H
*** 偵測到 SAFE MODE 按鈕按下 (GPIO 34) ***
*** 將清除 NVS 配置並使用預設值啟動 ***
NVS 配置已清除!設備將使用預設 Wi-Fi/MQTT 連線。
NVS 中未找到配置,將使用預設值。
當前配置 LED PIN: 2
當前配置 SSID: alex9ufo
當前配置 Broker: broker.mqtt-dashboard.com
嘗試連接到 SSID: alex9ufo
..
WiFi 連線成功
IP: 10.143.40.112
嘗試 MQTT 連線到 broker.mqtt-dashboard.com...成功!










沒有留言:
張貼留言