2025年11月30日 星期日

ESP-IDF: ESP32 MFRC522 RFID (VS Code)

 ESP-IDF: ESP32 MFRC522 RFID  (VS Code)









[env:esp32doit-devkit-v1]
platform = espressif32
board = esp32doit-devkit-v1
framework = arduino
monitor_speed = 115200

; **重要:移除 RST 腳位的 build_flags**
; 因為新的 MFRC522 函式庫在程式碼中定義 RST 腳位,而不是在 build_flags 中。
; build_flags = -D MFRC522_RST_PIN=27
build_flags =

; *** 修正函式庫:切換到穩定且兼容的 MFRC522 函式庫 (ID: 602) ***
lib_deps =
    MFRC522@^1.4.0


#include <Arduino.h>
#include <SPI.h>     // 必須包含 SPI 函式庫
#include <MFRC522.h> // *** 變更為新的函式庫標頭檔 ***

// 定義 MFRC522 的腳位
// 請確保您的接線如下:
// SDA/SS -> GPIO 5
// RST    -> GPIO 27
#define SS_PIN 5
#define RST_PIN 27

/***
在您上傳程式前,請確保 MFRC522 模組已按照以下腳位連接到您的 ESP32 開發板:
MFRC522 Pin 函式      ESP32 GPIO Pin (預設 SPI & 自訂 SS/RST)
SDA(SS)   Slave Select    GPIO 5 (程式碼中定義)
SCK   Serial Clock    GPIO 18 (ESP32 預設 SPI SCK)
MOSI    Master Out Slave  InGPIO 23 (ESP32 預設 SPI MOSI)
MISO    Master In Slave Out GPIO 19 (ESP32 預設 SPI MISO)
RST   Reset     GPIO 27 (程式碼中定義)
VCC   電源      3.3V (重要:MFRC522 應使用 3.3V)
GND   接地      GND
***/

// 創建 MFRC522 實例 (使用 SS 和 RST 腳位定義)
MFRC522 mfrc522(SS_PIN, RST_PIN);

void setup() {
  Serial.begin(115200);
  while (!Serial) {
    delay(10);
  }

  // 初始化 SPI 匯流排
  SPI.begin();
  // 初始化 MFRC522 讀卡器
  mfrc522.PCD_Init();    

  Serial.println(F("--- MFRC522 讀卡器初始化成功 ---"));
  Serial.println(F("請將 RFID 卡片或標籤靠近讀取器..."));
}


void loop() {
  // 檢查是否有新的卡片存在
  if (!mfrc522.PICC_IsNewCardPresent()) {
    return;
  }

  // 選擇一張卡片
  if (!mfrc522.PICC_ReadCardSerial()) {
    return;
  }
 
  Serial.println(F("\n=================================="));
  Serial.println(F(">>> 偵測到卡片 <<<"));
 
  // 輸出 PICC Type
  Serial.print(F("PICC Type: "));
  // 獲取卡片類型
  MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak);
  // **將卡片類型轉換為可讀字串 (修正函數名稱)**
  Serial.println(mfrc522.PICC_GetTypeName(piccType)); // <-- 這裡已修正
 
  // 輸出 UID
  Serial.print(F("UID (HEX): "));
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    // 以 16 進位輸出每個位元組
    Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
    Serial.print(mfrc522.uid.uidByte[i], HEX);
  }
  Serial.println();
 
  // 停止卡片通訊
  mfrc522.PICC_HaltA();
 
  delay(1000);
  Serial.println(F("=================================="));
}


2025年11月27日 星期四

Python 人機介面 設定Wifi ,MQTT 參數 控制 自訂的GPIO ON,OFF,FLASH,TIMER(20S)

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 並重新啟動,從而恢復正常操作。



  • #include <Arduino.h>
    #include <WiFi.h>
    #include <PubSubClient.h>
    #include <Preferences.h>
    #include <ArduinoJson.h>

    // 專門用於接收配置的主題,必須硬編碼
    const char* TOPIC_CONFIG = "alex9ufo/rfid/config";
    const int MQTT_PORT = 1883;

    // --- 安全模式設定 ---
    // 使用 GPIO 34 (BOOT 按鈕) 作為安全模式啟動按鈕
    const int SAFE_MODE_PIN = 34;
    const int SAFE_MODE_TRIGGER_STATE = LOW;

    // --- 全域變數 ---
    WiFiClient espClient;
    PubSubClient client(espClient);
    Preferences preferences;

    // 配置結構體
    struct DeviceConfig {
        int LED_PIN;
        char ssid[32];
        char password[64];
        char broker[64];
        char controlTopic[64];
        char statusTopic[64];
    } config;

    // LED 狀態變數
    enum LedMode { OFF, ON, FLASH, TIMER_ACTIVE };
    LedMode currentMode = OFF;
    unsigned long previousMillis = 0;
    const long flashInterval = 500;
    unsigned long timerStartTime = 0;
    const unsigned long timerDuration = 20000; // 20 秒

    // --- 函式宣告 ---
    void setup_default_config();
    void load_config();
    void save_config();
    void clear_config();
    void check_safe_mode();
    void setup_wifi();
    void callback(char* topic, byte* payload, unsigned int length);
    void reconnect();
    void publishLedStatus(const char* status);
    void handleTimer();
    void handleConfig(byte* payload, unsigned int length);

    void setup() {
        Serial.begin(115200);
       
        // !!! 新增:檢查是否進入安全模式 (按住 BOOT 鍵啟動) !!!
        check_safe_mode();
       
        // 1. 載入或設定配置
        setup_default_config();
        load_config();          
       
        // 2. 設置 LED 腳位 (根據配置)
        pinMode(config.LED_PIN, OUTPUT);
        digitalWrite(config.LED_PIN, LOW);
       
        Serial.print("當前配置 LED PIN: "); Serial.println(config.LED_PIN);
        Serial.print("當前配置 SSID: "); Serial.println(config.ssid);
        Serial.print("當前配置 Broker: "); Serial.println(config.broker);

        // 3. 連接 Wi-Fi
        setup_wifi();
       
        // 4. 設置 MQTT
        client.setServer(config.broker, MQTT_PORT);
        client.setCallback(callback);
    }

    void loop() {
        if (!client.connected()) {
            reconnect();
        }
        client.loop();

        // 處理閃爍邏輯 (FLASH)
        if (currentMode == FLASH) {
            unsigned long currentMillis = millis();
            if (currentMillis - previousMillis >= flashInterval) {
                previousMillis = currentMillis;
                int ledState = digitalRead(config.LED_PIN);
                digitalWrite(config.LED_PIN, !ledState);
            }
        }
       
        // 處理計時器邏輯 (TIMER)
        handleTimer();
    }

    // --- 配置和安全模式處理函式 ---

    /**
     * @brief 檢查按鈕是否按下,以進入安全模式並清除NVS
     */
    void check_safe_mode() {
        pinMode(SAFE_MODE_PIN, INPUT_PULLUP);
        delay(50);

        if (digitalRead(SAFE_MODE_PIN) == SAFE_MODE_TRIGGER_STATE) {
            Serial.println("\n*** 偵測到 SAFE MODE 按鈕按下 (GPIO 34) ***");
            Serial.println("*** 將清除 NVS 配置並使用預設值啟動 ***");
            delay(1000);
            clear_config();
        }
    }

    /**
     * @brief 清除整個 NVS 命名空間的配置
     */
    void clear_config() {
        preferences.begin("device-cfg", false);
        preferences.clear();
        preferences.end();
        Serial.println("NVS 配置已清除!設備將使用預設 Wi-Fi/MQTT 連線。");
    }

    /**
     * @brief 設定硬編碼預設配置
     */
    void setup_default_config() {
        config.LED_PIN = 2; // ESP32 預設板載 LED
        strcpy(config.ssid, "alex9ufo");
        strcpy(config.password, "alex9981");
        strcpy(config.broker, "broker.mqtt-dashboard.com");
        strcpy(config.controlTopic, "alex9ufo/rfid/led");
        strcpy(config.statusTopic, "alex9ufo/rfid/ledStatus");
    }

    /**
     * @brief 從 NVS 載入配置 (已修正 isKey 錯誤)
     */
    void load_config() {
        preferences.begin("device-cfg", true); // 只讀模式
       
        if (preferences.isKey("ssid")) {
            Serial.println("從 NVS 載入配置...");
            config.LED_PIN = preferences.getInt("led_pin", config.LED_PIN);
            preferences.getString("ssid", config.ssid, sizeof(config.ssid));
            preferences.getString("password", config.password, sizeof(config.password));
            preferences.getString("broker", config.broker, sizeof(config.broker));
            preferences.getString("control", config.controlTopic, sizeof(config.controlTopic));
            preferences.getString("status", config.statusTopic, sizeof(config.statusTopic));
        } else {
            Serial.println("NVS 中未找到配置,將使用預設值。");
        }
        preferences.end();
    }

    /**
     * @brief 儲存當前配置到 NVS
     */
    void save_config() {
        Serial.println("正在儲存配置到 NVS...");
        preferences.begin("device-cfg", false); // 讀寫模式
        preferences.putInt("led_pin", config.LED_PIN);
        preferences.putString("ssid", config.ssid);
        preferences.putString("password", config.password);
        preferences.putString("broker", config.broker);
        preferences.putString("control", config.controlTopic);
        preferences.putString("status", config.statusTopic);
        preferences.end();
        Serial.println("配置儲存完成。");
    }

    // --- 通訊和控制函式 ---

    void setup_wifi() {
        Serial.print("嘗試連接到 SSID: ");
        Serial.println(config.ssid);
        WiFi.begin(config.ssid, config.password);

        int attempts = 0;
        while (WiFi.status() != WL_CONNECTED && attempts < 20) {
            delay(1000);
            Serial.print(".");
            attempts++;
        }

        if (WiFi.status() == WL_CONNECTED) {
            Serial.println("\nWiFi 連線成功");
            Serial.print("IP: "); Serial.println(WiFi.localIP());
        } else {
            Serial.println("\nWiFi 連線失敗 (可能 SSID/Password 錯誤)");
        }
    }

    void reconnect() {
        while (!client.connected()) {
            Serial.print("嘗試 MQTT 連線到 ");
            Serial.print(config.broker);
            Serial.print("...");

            String clientId = "ESP32_";
            clientId += String(random(0xffff), HEX);
           
            if (client.connect(clientId.c_str())) {
                Serial.println("成功!");
                // 訂閱控制主題和配置主題
                client.subscribe(config.controlTopic);
                client.subscribe(TOPIC_CONFIG);

                publishLedStatus("READY");
            } else {
                Serial.print("失敗, rc=");
                Serial.print(client.state());
                Serial.println(" 5 秒後重試 (可能 Broker 地址/DNS 錯誤)");
                delay(5000);
            }
        }
    }

    void callback(char* topic, byte* payload, unsigned int length) {
       
        // 1. 檢查是否為配置主題
        if (strcmp(topic, TOPIC_CONFIG) == 0) {
            handleConfig(payload, length);
            return;
        }

        // 2. LED 控制邏輯
        if (strcmp(topic, config.controlTopic) == 0) {
            String message;
            for (int i = 0; i < length; i++) {
                message += (char)payload[i];
            }

            if (message == "on") {
                currentMode = ON;
                digitalWrite(config.LED_PIN, HIGH);
                publishLedStatus("ON");
            } else if (message == "off") {
                currentMode = OFF;
                digitalWrite(config.LED_PIN, LOW);
                publishLedStatus("OFF");
            } else if (message == "flash") {
                currentMode = FLASH;
                publishLedStatus("FLASH");
            } else if (message == "timer_20") {
                currentMode = TIMER_ACTIVE;
                timerStartTime = millis();
                digitalWrite(config.LED_PIN, HIGH);
                publishLedStatus("TIMER_20S");
            }
        }
    }

    void handleConfig(byte* payload, unsigned int length) {
        StaticJsonDocument<1024> doc;
        DeserializationError error = deserializeJson(doc, payload, length);

        if (error) {
            Serial.print(F("deserializeJson() 失敗: "));
            Serial.println(error.f_str());
            return;
        }

        // 檢查並更新結構體
        if (doc.containsKey("LED_PIN")) {
            config.LED_PIN = doc["LED_PIN"].as<int>();
        }
        if (doc.containsKey("ssid")) {
            strlcpy(config.ssid, doc["ssid"] | "", sizeof(config.ssid));
        }
        if (doc.containsKey("password")) {
            strlcpy(config.password, doc["password"] | "", sizeof(config.password));
        }
        if (doc.containsKey("broker")) {
            strlcpy(config.broker, doc["broker"] | "", sizeof(config.broker));
        }
        if (doc.containsKey("control_topic")) {
            strlcpy(config.controlTopic, doc["control_topic"] | "", sizeof(config.controlTopic));
        }
        if (doc.containsKey("status_topic")) {
            strlcpy(config.statusTopic, doc["status_topic"] | "", sizeof(config.statusTopic));
        }

        // 儲存新配置
        save_config();

        // 提示並重新啟動以應用新配置
        Serial.println("新配置已儲存。ESP32 將在 3 秒後重新啟動...");
        delay(3000);
        ESP.restart();
    }


    void publishLedStatus(const char* status) {
        if (client.connected()) {
            client.publish(config.statusTopic, status);
        }
    }

    void handleTimer() {
        if (currentMode == TIMER_ACTIVE) {
            if (millis() - timerStartTime >= timerDuration) {
                currentMode = OFF;
                digitalWrite(config.LED_PIN, LOW);
                publishLedStatus("OFF_TIMER_DONE");
            }
        }
    }


    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...成功!






      清除項目編譯快取 執行 flutter clean 重新取得套件依賴 執行 flutter pub get 完整重新編譯並執行 flutter run 1. 新增專案套件 (pubspec.yaml) 在 Flutter 專案根目錄下的 pubspec.yaml 中,新增 ...