2026年5月22日 星期五




WOKWI程式

// 定義 MFRC522 RFID read 與 ESP32 介面 接腳連接 Pin assign

/* Wiring RFID RC522 module   

==============================================================

GND     = GND   3.3V    = 3.3V

The following table shows the typical pin layout used:

 * MFRC522      ESP32     

 * Reader/PCD             

 * Signal      Pin          Pin         

 * -----------------------------------

 * RST/Reset   RST          GPIO21   

 * SPI SS      SDA(SS)      GPIO5     

 * SPI MOSI    MOSI         GPIO23    

 * SPI MISO    MISO         GPIO19    

 * SPI SCK     SCK          GPIO18    

=============================================================

* I2C LCD 16x2 接線:

* SDA  --> GPIO 17

* SCL  --> GPIO 16

=============================================================

*/


// Wifi 與 MQttClient 程式庫

#include <WiFi.h>

#include <ArduinoMqttClient.h>


// MFRC522 程式庫

#include <SPI.h>

#include <MFRC522.h>


// I2C 與 LCD 程式庫

#include <Wire.h>

#include <LiquidCrystal_I2C.h>


#define LED 13           // 定義 LED 接腳


// 定義 I2C LCD 腳位與實例 (Wokwi 模擬的 I2C 位址通常為 0x27)

#define I2C_SDA 17

#define I2C_SCL 16

LiquidCrystal_I2C lcd(0x27, 16, 2); 


// === Wokwi 專屬的 Wi-Fi 設定 ===

char ssid[] = "Wokwi-GUEST";    

char pass[] = "";               


WiFiClient wifiClient;

MqttClient mqttClient(wifiClient);


// === MQTT Broker 設定 ===

const char broker[] = "broker.emqx.io"; 

int    port     = 1883;

String json = "";


// MQTT 主題定義

const char *SubTopic1 = "alex9ufo/2026/RFID/LED";

const char *PubTopic2 = "alex9ufo/2026/RFID/Back_LED";

const char *PubTopic3 = "alex9ufo/2026/RFID/RFID_UID";

const char *PubTopic4 = "alex9ufo/2026/RFID/RFID_PICC";

const char willTopic[] = "alex9ufo/2026/RFID/Starting";


//======================================================

#define RST_PIN      21        

#define SS_PIN       5         

MFRC522 rfid(SS_PIN, RST_PIN);

MFRC522::MIFARE_Key key;  

MFRC522::StatusCode status;


//===========================================================

bool Send = false;  

String LEDjson = "OFF"; // 預設狀態為 OFF


// --- 非阻塞計時器變數 ---

unsigned long lcdResetTimer = 0;

bool lcdShowingCard = false;

const unsigned long displayDuration = 2000; // 卡片資訊在 LCD 顯示的時間 (2秒)


//===========================================================

// MQTT 訂閱訊息接收回呼函式 (當遠端發送指令給 ESP32 時觸發)

void onMqttMessage(int messageSize) {

  Serial.print("Received a message with topic '");

  Serial.print(mqttClient.messageTopic());

  String Topic = mqttClient.messageTopic();

  Serial.print("', length ");

  Serial.print(messageSize);

  Serial.println(" bytes:");

  

  String message = "";

  while (mqttClient.available()) {

    message += (char)mqttClient.read();

  }


  Serial.println(message);

  message.trim();

  Topic.trim();


  if (Topic == "alex9ufo/2026/RFID/LED") {

    if (message == "on") {

      digitalWrite(LED, LOW);  // 點亮 LED (低電位觸發)

      LEDjson = "ON";

      Send = true;

    }

    if (message == "off") {

      digitalWrite(LED, HIGH); // 熄滅 LED

      LEDjson = "OFF";

      Send = true;

    }

    

    // 如果目前 LCD 不是在顯示剛刷卡的資訊,就即時更新 LCD 第二行

    if (!lcdShowingCard) {

      lcd.setCursor(0, 1);

      lcd.print("LED: " + LEDjson + "        "); 

    }

    

    Serial.print("LED = ");

    Serial.println(LEDjson);

    Serial.println("\n-----------------------");

  }  

}


//===========================================================

// 16進位 Byte 陣列轉字串工具

String printHex(byte *buffer, byte bufferSize) {

  String id = "";

  for (byte i = 0; i < bufferSize; i++) {

    id += buffer[i] < 0x10 ? "0" : "";

    id += String(buffer[i], HEX);

    id += " ";

  }

  return id;

}


//===========================================================

// Wi-Fi 連線程序

void setup_wifi() {

  delay(10);

  Serial.println();

  Serial.print("Connecting to ");

  Serial.println(ssid);

  WiFi.begin(ssid, pass);

  

  lcd.clear();

  lcd.setCursor(0, 0);

  lcd.print("Connecting WiFi");


  int dotCount = 0;

  while (WiFi.status() != WL_CONNECTED) {

    delay(500);

    Serial.print(".");

    lcd.setCursor(dotCount % 16, 1);

    lcd.print(".");

    dotCount++;

  }


  Serial.println("\nWiFi connected");

  Serial.print("IP address: ");

  Serial.println(WiFi.localIP());


  lcd.clear();

  lcd.setCursor(0, 0);

  lcd.print("WiFi Connected!");

  lcd.setCursor(0, 1);

  lcd.print(WiFi.localIP().toString());

  delay(1500);

}  


//===========================================================

// MQTT 發行:回報當前 LED 狀態

void LED_Message() {

  if (Send) {

    Serial.print("Publish message: ");

    Serial.println(LEDjson);

    LEDjson.trim();


    int qos = 0; // 改為 QoS 0 提高在公開 Broker 上的傳輸穩定度

    bool retained = false;

    bool dup = false;

    

    mqttClient.beginMessage(PubTopic2, LEDjson.length(), retained, qos, dup);

    mqttClient.print(LEDjson);

    mqttClient.endMessage();

    Send = false;

  }


//=========================================================== 

void setup() {

  pinMode(LED, OUTPUT);

  digitalWrite(LED, HIGH); // 初始關閉 LED

  

  Serial.begin(115200);

  while (!Serial);

  

  // 初始化自訂的 I2C 接腳 (SDA=17, SCL=16)

  Wire.begin(I2C_SDA, I2C_SCL);

  

  // 初始化 LCD

  lcd.init();

  lcd.backlight();

  lcd.print("System Init...");


  setup_wifi();

  

  // 設定遺言訊息 (Last Will)

  String willPayload = "ESP32 Disconnected";

  bool willRetain = false;

  int willQos = 0;

  mqttClient.beginWill(willTopic, willPayload.length(), willRetain, willQos);

  mqttClient.print(willPayload);

  mqttClient.endWill();


  Serial.print("Attempting to connect to the MQTT broker: ");

  Serial.println(broker);


  lcd.clear();

  lcd.print("Connect MQTT...");

  if (!mqttClient.connect(broker, port)) {

    Serial.print("MQTT connection failed! Error code = ");

    Serial.println(mqttClient.connectError());

    lcd.clear();

    lcd.print("MQTT Conn Fail!");

    while (1);

  }


  Serial.println("You're connected to the MQTT broker!\n");


  // 註冊訂閱接收市場與訂閱主題

  mqttClient.onMessage(onMqttMessage);

  int subscribeQos = 0; 

  mqttClient.subscribe(SubTopic1, subscribeQos);


  // 初始化 RFID 模組

  SPI.begin();         

  rfid.PCD_Init();     

  delay(4);

  Serial.println("Scan PICC to see UID...");


  // 進入主迴圈前的初始畫面

  lcd.clear();

  lcd.setCursor(0, 0);

  lcd.print("Scan RFID Card..");

  lcd.setCursor(0, 1);

  lcd.print("LED: OFF");

}


//===========================================================

void loop() {

  // 保持 MQTT 心跳與背景訊息接收(非阻塞核心)

  mqttClient.poll();

  

  // 處理 LED 狀態發行回報

  LED_Message();


  // --- LCD 畫面自動復原計時檢查 ---

  if (lcdShowingCard && (millis() - lcdResetTimer >= displayDuration)) {

    lcdShowingCard = false;

    lcd.clear();

    lcd.setCursor(0, 0);

    lcd.print("Scan RFID Card..");

    lcd.setCursor(0, 1);

    lcd.print("LED: " + LEDjson + "        ");

  }


  // 檢查是否有新卡片被擺放,以及是否成功讀取

  if (rfid.PICC_IsNewCardPresent() && rfid.PICC_ReadCardSerial()) {

    

    byte *id = rfid.uid.uidByte;   

    byte idSize = rfid.uid.size;   

    String Type;

    

    MFRC522::PICC_Type piccType = rfid.PICC_GetType(rfid.uid.sak);

    Type = rfid.PICC_GetTypeName(piccType);


    // 格式化 UID 字串

    json = printHex(rfid.uid.uidByte, rfid.uid.size);

    json.toUpperCase();

    json.trim();


    // ====== LCD 顯示邏輯更新 ======

    lcd.clear();

    lcd.setCursor(0, 0);

    lcd.print("ID: " + json);                 // 第一行顯示 UID

    lcd.setCursor(0, 1);

    lcd.print("Type:" + Type.substring(0, 11)); // 第二行顯示卡片類型 (限制長度防止溢出)

    

    // 啟動非阻塞計時器,標記目前正在展示卡片

    lcdResetTimer = millis();

    lcdShowingCard = true;

    // =============================


    // 發送 MQTT:卡片 UID 資訊

    int qos = 0;

    bool retained = false;

    bool dup = false;

    mqttClient.beginMessage(PubTopic3, json.length(), retained, qos, dup);

    mqttClient.print(json);

    mqttClient.endMessage();


    // 發送 MQTT:卡片類型資訊

    String typeJson = "PICC type: " + Type;

    typeJson.trim();

    mqttClient.beginMessage(PubTopic4, typeJson.length(), retained, qos, dup);

    mqttClient.print(typeJson);

    mqttClient.endMessage();

    

    // 本機序列埠除錯輸出

    Serial.println("Published UID: " + json);

    Serial.println("Published Type: " + typeJson);

    Serial.println();


    // 讓卡片進入休眠,避免重複讀取

    rfid.PICC_HaltA();

    rfid.PCD_StopCrypto1(); 

  }

}



這份程式碼是經過優化後的完整版本,主要特點是結合了 MFRC522 RFID 讀卡I2C LCD 16x2 顯示,以及基於 millis() 非阻塞計時器的 MQTT 雙向通訊

以下為您進行這份優化版程式碼的逐行詳細說明:

1. 硬體連接與備忘註解

C++
// 定義 MFRC522 RFID read 與 ESP32 介面 接腳連接 Pin assign
/* Wiring RFID RC522 module   
==============================================================
GND     = GND   3.3V    = 3.3V
The following table shows the typical pin layout used:
 * MFRC522      ESP32     
 * Reader/PCD             
 * Signal      Pin          Pin         
 * -----------------------------------
 * RST/Reset   RST          GPIO21   
 * SPI SS      SDA(SS)      GPIO5     
 * SPI MOSI    MOSI         GPIO23    
 * SPI MISO    MISO         GPIO19    
 * SPI SCK     SCK          GPIO18    
=============================================================
* I2C LCD 16x2 接線:
* SDA  --> GPIO 17
* SCL  --> GPIO 16
=============================================================
*/
  • 說明:純文字註解,紀錄實體硬體或 Wokwi 模擬器上的接線定義。RFID 使用標準 VSPI 腳位(RST 改用 21);I2C LCD 則接在 GPIO 17 與 16。

2. 引入程式庫

C++
// Wifi 與 MQttClient 程式庫
#include <WiFi.h>
#include <ArduinoMqttClient.h>

// MFRC522 程式庫
#include <SPI.h>
#include <MFRC522.h>

// I2C 與 LCD 程式庫
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
  • #include <WiFi.h> / <ArduinoMqttClient.h>:提供 Wi-Fi 連線與官方 MQTT 協定支援。

  • #include <SPI.h> / <MFRC522.h>:驅動以 SPI 介面通訊的 RFID 讀卡機。

  • #include <Wire.h> / <LiquidCrystal_I2C.h>:驅動 I2C 介面的液晶顯示器(LCD)。

3. 引腳、物件與 MQTT 主題定義

C++
#define LED 13           // 定義 LED 接腳

// 定義 I2C LCD 腳位與實例 (Wokwi 模擬的 I2C 位址通常為 0x27)
#define I2C_SDA 17
#define I2C_SCL 16
LiquidCrystal_I2C lcd(0x27, 16, 2); 
  • #define LED 13:LED 連接到 GPIO 13。

  • #define I2C_SDA 17 / #define I2C_SCL 16:自訂 ESP32 的 I2C 腳位。

  • LiquidCrystal_I2C lcd(0x27, 16, 2);:初始化 LCD 物件,設定 I2C 位址為 0x27,規格為 16 欄 2 列。

C++
// === Wokwi 專屬的 Wi-Fi 設定 ===
char ssid[] = "Wokwi-GUEST";    
char pass[] = "";               

WiFiClient wifiClient;
MqttClient mqttClient(wifiClient);
  • 設定 Wokwi 線上模擬專用的模擬基地台 SSID 與無密碼設定。

  • 建立底層 TCP 連線(wifiClient)並將其包裝進 MQTT 客戶端(mqttClient)。

C++
// === MQTT Broker 設定 ===
const char broker[] = "broker.emqx.io"; 
int    port     = 1883;
String json = "";

// MQTT 主題定義
const char *SubTopic1 = "alex9ufo/2026/RFID/LED";
const char *PubTopic2 = "alex9ufo/2026/RFID/Back_LED";
const char *PubTopic3 = "alex9ufo/2026/RFID/RFID_UID";
const char *PubTopic4 = "alex9ufo/2026/RFID/RFID_PICC";
const char willTopic[] = "alex9ufo/2026/RFID/Starting";
  • 定義 EMQX 公開伺服器網址與預設的非加密通訊埠 1883

  • SubTopic1訂閱接收控制 LED 指令的主題。

  • PubTopic2 ~ 4willTopic發行狀態、UID、卡片類型與遺言的主題。

C++
//======================================================
#define RST_PIN      21        
#define SS_PIN       5         
MFRC522 rfid(SS_PIN, RST_PIN);
MFRC522::MIFARE_Key key;  
MFRC522::StatusCode status;
  • 建立 MFRC522 讀卡機實例,傳入晶片選擇引腳(5)與重置引腳(21)。

  • 宣告金鑰(key)與狀態變數(status)供後續呼叫。

4. 全域變數與非阻塞計時器宣告

C++
bool Send = false;  
String LEDjson = "OFF"; // 預設狀態為 OFF

// --- 非阻塞計時器變數 ---
unsigned long lcdResetTimer = 0;
bool lcdShowingCard = false;
const unsigned long displayDuration = 2000; // 卡片資訊在 LCD 顯示的時間 (2秒)
  • Send:觸發發行 LED 狀態的旗標。

  • LEDjson:儲存當前 LED 狀態("ON" 或 "OFF")。

  • lcdResetTimer:紀錄刷卡時的時間戳記(單位:毫秒)。

  • lcdShowingCard:布林旗標,用來標記「目前 LCD 是否正停留在顯示 UID 畫面上」。

  • displayDuration:設定刷卡資訊要在 LCD 上停留多久(2000 毫秒 = 2 秒)。

5. 功能函式與內部邏輯

A. MQTT 訂閱訊息回呼 (Callback) 函式

C++
void onMqttMessage(int messageSize) {
  Serial.print("Received a message with topic '");
  Serial.print(mqttClient.messageTopic());
  String Topic = mqttClient.messageTopic();
  Serial.print("', length ");
  Serial.print(messageSize);
  Serial.println(" bytes:");
  • 當遠端有人發布訊息到 ESP32 訂閱的主題時,此函式會被自動觸發。在序列埠輸出收到的主題與訊息大小。

C++
  String message = "";
  while (mqttClient.available()) {
    message += (char)mqttClient.read();
  }

  Serial.println(message);
  message.trim();
  Topic.trim();
  • 透過迴圈讀取快取中的字元,拼裝成 message 字串,並使用 .trim() 清除字串前後的多餘空白或換行符號。

C++
  if (Topic == "alex9ufo/2026/RFID/LED") {
    if (message == "on") {
      digitalWrite(LED, LOW);  // 點亮 LED (低電位觸發)
      LEDjson = "ON";
      Send = true;
    }
    if (message == "off") {
      digitalWrite(LED, HIGH); // 熄滅 LED
      LEDjson = "OFF";
      Send = true;
    }
  • 比對主題。如果收到小寫的 "on",將 GPIO 13 輸出低電位點亮 LED(許多開發板為低電位觸發),變數改為 "ON",並將 Send 設為 true 準備回報。反之收到 "off" 則關燈。

C++
    // 如果目前 LCD 不是在顯示剛刷卡的資訊,就即時更新 LCD 第二行
    if (!lcdShowingCard) {
      lcd.setCursor(0, 1);
      lcd.print("LED: " + LEDjson + "        "); 
    }
    
    Serial.print("LED = ");
    Serial.println(LEDjson);
    Serial.println("\n-----------------------");
  }  
}
  • 邏輯檢查:如果 LCD 此刻沒有在顯示剛刷完卡的 UID 資訊(避免畫面被蓋掉),就立刻在 LCD 第二行刷新顯示目前的 LED 狀態,後方留空格用以覆蓋舊字。

B. 16 進位字串轉換工具

C++
String printHex(byte *buffer, byte bufferSize) {
  String id = "";
  for (byte i = 0; i < bufferSize; i++) {
    id += buffer[i] < 0x10 ? "0" : "";
    id += String(buffer[i], HEX);
    id += " ";
  }
  return id;
}
  • 將 RFID 晶片讀出的原始二進位 Byte 陣列,逐一轉換成大寫 16 進位英數字串(補零處理,如 0A B3 2C),方便顯示與閱讀。

C. Wi-Fi 連線程序

C++
void setup_wifi() {
  delay(10);
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, pass);
  
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Connecting WiFi");

  int dotCount = 0;
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    lcd.setCursor(dotCount % 16, 1);
    lcd.print(".");
    dotCount++;
  }
  • 開始連接無線網路,並同時在 LCD 第一行印出 "Connecting WiFi"

  • 運用 while 迴圈每 0.5 秒檢查連線狀態,連線上之前,會在 LCD 第二行像跑馬燈一樣橫向列印點記號 .

C++
  Serial.println("\nWiFi connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("WiFi Connected!");
  lcd.setCursor(0, 1);
  lcd.print(WiFi.localIP().toString());
  delay(1500);
}  
  • 連線成功後,本機序列埠輸出成功訊息,並在 LCD 上短暫顯示獲取的局域網路 IP 位址 1.5 秒。

D. MQTT 狀態發行 (回報 LED 狀態)

C++
void LED_Message() {
  if (Send) {
    Serial.print("Publish message: ");
    Serial.println(LEDjson);
    LEDjson.trim();

    int qos = 0; // 改為 QoS 0 提高在公開 Broker 上的傳輸穩定度
    bool retained = false;
    bool dup = false;
    
    mqttClient.beginMessage(PubTopic2, LEDjson.length(), retained, qos, dup);
    mqttClient.print(LEDjson);
    mqttClient.endMessage();
    Send = false;
  }
} 
  • 檢查 Send 旗標。若成立,則將當前最新的 LED 狀態字串發布至 PubTopic2.../Back_LED)。

  • 關鍵優化:此處將服務品質改為 qos = 0,這對公開且高流量的 broker.emqx.io 伺服器而言,可以大幅提高訊息成功收發的順暢度、降低連線被剔除的機率。

6. 初始化設定 (setup)

C++
void setup() {
  pinMode(LED, OUTPUT);
  digitalWrite(LED, HIGH); // 初始關閉 LED
  
  Serial.begin(115200);
  while (!Serial);
  
  // 初始化自訂的 I2C 接腳 (SDA=17, SCL=16)
  Wire.begin(I2C_SDA, I2C_SCL);
  
  // 初始化 LCD
  lcd.init();
  lcd.backlight();
  lcd.print("System Init...");
  • 配置 LED 為輸出腳位並預設關燈。開啟 115200 鮑率的序列埠。

  • 呼叫 Wire.begin(17, 16) 強制指定 I2C 介面的 SDA 與 SCL 硬體映射引腳。

  • 初始化 LCD 並點亮其背光,顯示 "System Init..."

C++
  setup_wifi();
  
  // 設定遺言訊息 (Last Will)
  String willPayload = "ESP32 Disconnected";
  bool willRetain = false;
  int willQos = 0;
  mqttClient.beginWill(willTopic, willPayload.length(), willRetain, willQos);
  mqttClient.print(willPayload);
  mqttClient.endWill();
  • 呼叫 Wi-Fi 連線。

  • 配置 MQTT 「遺言(Last Will)」機制。若設備未來發生異常斷線,伺服器會替它向 willTopic 發布 "ESP32 Disconnected" 訊息。

C++
  Serial.print("Attempting to connect to the MQTT broker: ");
  Serial.println(broker);

  lcd.clear();
  lcd.print("Connect MQTT...");
  if (!mqttClient.connect(broker, port)) {
    Serial.print("MQTT connection failed! Error code = ");
    Serial.println(mqttClient.connectError());
    lcd.clear();
    lcd.print("MQTT Conn Fail!");
    while (1);
  }

  Serial.println("You're connected to the MQTT broker!\n");
  • 在 LCD 提示下嘗試連線至 MQTT Broker。如果連線失敗,則在 LCD 上顯示錯誤並進入死迴圈停機;成功則繼續。

C++
  // 註冊訂閱接收市場與訂閱主題
  mqttClient.onMessage(onMqttMessage);
  int subscribeQos = 0; 
  mqttClient.subscribe(SubTopic1, subscribeQos);

  // 初始化 RFID 模組
  SPI.begin();         
  rfid.PCD_Init();     
  delay(4);
  Serial.println("Scan PICC to see UID...");

  // 進入主迴圈前的初始畫面
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Scan RFID Card..");
  lcd.setCursor(0, 1);
  lcd.print("LED: OFF");
}
  • 訂閱 LED 控制主題(QoS 改為 0)。

  • 開啟 SPI 匯流排並驅動 MFRC522 晶片初始化。

  • 清除 LCD 畫面,將其設為待命刷卡狀態(第一行 Scan RFID Card..,第二行 LED: OFF)。

7. 主程式工作迴圈 (loop)

C++
void loop() {
  // 保持 MQTT 心跳與背景訊息接收(非阻塞核心)
  mqttClient.poll();
  
  // 處理 LED 狀態發行回報
  LED_Message();
  • mqttClient.poll():每毫秒高頻維持與遠端 Broker 的長連線,並檢查有無最新訂閱訊息。

  • 隨時檢查有無 LED 狀態需要發行回報。

C++
  // --- LCD 畫面自動復原計時檢查 ---
  if (lcdShowingCard && (millis() - lcdResetTimer >= displayDuration)) {
    lcdShowingCard = false;
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Scan RFID Card..");
    lcd.setCursor(0, 1);
    lcd.print("LED: " + LEDjson + "        ");
  }
  • 關鍵計時邏輯(非阻塞式):如果目前 LCD 正在顯示刷卡資訊(lcdShowingCard == true),程式不會用 delay() 卡死,而是用現時時間(millis())減去刷卡當下的時間。

  • 一旦滿 2000 毫秒(2 秒),會自動把標記設回 false,並將 LCD 復原為預設待命畫面,同時顯示當下真實的 LED 狀態。這確保了在等待這 2 秒的期間,MQTT 指令依然進得來、LED 依然能被控制

C++
  // 檢查是否有新卡片被擺放,以及是否成功讀取
  if (rfid.PICC_IsNewCardPresent() && rfid.PICC_ReadCardSerial()) {
    
    byte *id = rfid.uid.uidByte;   
    byte idSize = rfid.uid.size;   
    String Type;
    
    MFRC522::PICC_Type piccType = rfid.PICC_GetType(rfid.uid.sak);
    Type = rfid.PICC_GetTypeName(piccType);

    // 格式化 UID 字串
    json = printHex(rfid.uid.uidByte, rfid.uid.size);
    json.toUpperCase();
    json.trim();
  • 偵測到有新卡片放入且成功讀取序列號。

  • 辨識卡片 SAK 規格取得類型,並呼叫 printHex 轉換出 UID 16 進位字串。

C++
    // ====== LCD 顯示邏輯更新 ======
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("ID: " + json);                 // 第一行顯示 UID
    lcd.setCursor(0, 1);
    lcd.print("Type:" + Type.substring(0, 11)); // 第二行顯示卡片類型 (限制長度防止溢出)
    
    // 啟動非阻塞計時器,標記目前正在展示卡片
    lcdResetTimer = millis();
    lcdShowingCard = true;
    // =============================
  • 清除 LCD 舊畫面。第一行印出 "ID: 8A 2F ..."

  • 第二行印出 "Type:" 加上截取前 11 個字元的類型名稱(防止字串過長溢出螢幕)。

  • 紀錄當前時間戳記到 lcdResetTimer,並將 lcdShowingCard 設為 true 啟動倒數。

C++
    // 發送 MQTT:卡片 UID 資訊
    int qos = 0;
    bool retained = false;
    bool dup = false;
    mqttClient.beginMessage(PubTopic3, json.length(), retained, qos, dup);
    mqttClient.print(json);
    mqttClient.endMessage();

    // 發送 MQTT:卡片類型資訊
    String typeJson = "PICC type: " + Type;
    typeJson.trim();
    mqttClient.beginMessage(PubTopic4, typeJson.length(), retained, qos, dup);
    mqttClient.print(typeJson);
    mqttClient.endMessage();
    
    // 本機序列埠除錯輸出
    Serial.println("Published UID: " + json);
    Serial.println("Published Type: " + typeJson);
    Serial.println();

    // 讓卡片進入休眠,避免重複讀取
    rfid.PICC_HaltA();
    rfid.PCD_StopCrypto1(); 
  }
}

  • 將讀取到的 UID 資訊與 PICC 類型字串分別發行至 PubTopic3PubTopic4

  • 序列埠輸出訊息以供監控除錯。

  • 執行卡片休眠命令(PICC_HaltA()PCD_StopCrypto1()),釋放與該感應卡的加密連線,等待下一次新卡片切入。

沒有留言:

張貼留言

WOKWI程式 // 定義 MFRC522 RFID read 與 ESP32 介面 接腳連接 Pin assign /* Wiring RFID RC522 module    ==================================================...