2024年1月29日 星期一

ESP32 MFRC522 RFID Reader 使用 ArduinoMqttClient 程式庫 執行 Node-Red + MQTT + 讀取Mifare卡片的資料

 ESP32 MFRC522 RFID Reader 使用 ArduinoMqttClient 程式庫 執行 Node-Red + MQTT +  讀取Mifare卡片的資料 















讀取與寫入資料到Mifare卡片

底下列舉本單元使用到的MFRC522程式物件的方法和屬性:

  • MFRC522物件.PCD_Authenticate():驗證金鑰,相當於比對輸入密碼和卡片裡的密碼,唯通過驗證才能存取區段資料。
  • MFRC522物件.GetStatusCodeName():取得狀態碼的名稱
  • MFRC522物件.MIFARE_Read():讀取指定區塊的內容
  • MFRC522物件.MIFARE_Write():在指定區塊寫入資料
  • MFRC522物件.PICC_DumpMifareClassicSectorToSerial():在序列埠監控視窗顯示指定的區段內容



/*
  ArduinoMqttClient - WiFi Simple Receive Callback

  This example connects to a MQTT broker and subscribes to a single topic.
  When a message is received it prints the message to the Serial Monitor,
  it uses the callback functionality of the library.

*/

#include <ArduinoMqttClient.h>
#include <WiFi.h>
#include "arduino_secrets.h"

///////please enter your sensitive data in the Secret tab/arduino_secrets.h
//char ssid[] = "TOTOLINK_A3002MU";    // your network SSID (name)
//char pass[] = "24063173";    // your network password (use for WPA, or use as key for WEP)

char ssid[] = "dlink-103A";    // your network SSID (name)
char pass[] = "bdcce12882";    // your network password (use for WPA, or use as key for WEP)


// To connect with SSL/TLS:
// 1) Change WiFiClient to WiFiSSLClient.
// 2) Change port value from 1883 to 8883.
// 3) Change broker value to a server with a known SSL/TLS root certificate
//    flashed in the WiFi module.

WiFiClient wifiClient;
MqttClient mqttClient(wifiClient);

//const char broker[] = "test.mosquitto.org";
const char broker[] = "broker.mqtt-dashboard.com";
int        port     = 1883;

const char willTopic[] = "alex9ufo/will";
const char inTopic[]   = "alex9ufo/in";
const char outTopic[]  = "alex9ufo/out";


const long interval = 30000;
unsigned long previousMillis = 0;
int count = 0;
#define LED 2   //定義LED接腳
//定義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          GPIO27  
 * SPI SS      SDA(SS)      GPIO5    
 * SPI MOSI    MOSI         GPIO23    
 * SPI MISO    MISO         GPIO19    
 * SPI SCK     SCK          GPIO18    
 *
[1] (1, 2) Configurable, typically defined as RST_PIN in sketch/program.
[2] (1, 2) Configurable, typically defined as SS_PIN in sketch/program.
[3] The SDA pin might be labeled SS on some/older MFRC522 boards
=============================================================
*/
//===========================================================
#include <SPI.h>
#include <MFRC522.h>

#define RST_PIN      27        // 讀卡機的重置腳位
#define SS_PIN       5        // 晶片選擇腳位
MFRC522 mfrc522(SS_PIN, RST_PIN);    // 建立MFRC522物件
MFRC522::MIFARE_Key key;  // 儲存金鑰
MFRC522::StatusCode status;
String json = "";
const char *topic1 = "alex9ufo/RFID/read";
const char *topic3 = "alex9ufo/RFID/back";

byte sector = 15;   // 指定讀寫的「區段」,可能值:0~15
byte block = 1;     // 指定讀寫的「區塊」,可能值:0~3
byte blockData[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} ;  
// 最多可存入16個字元
// 若要清除區塊內容,請寫入16個 0
//byte blockData[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
// 暫存讀取區塊內容的陣列,MIFARE_Read()方法要求至少要18位元組空間,來存放16位元組。
byte bufferarray[18] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } ;
bool RD_flag=false;
bool Pub_flag=false;
//===========================================================
void readBlock(byte _sector, byte _block, byte _blockData[])  {
  if (_sector < 0 || _sector > 15 || _block < 0 || _block > 3) {
    // 顯示「區段或區塊碼錯誤」,然後結束函式。
    // 判斷區段值是否介於0~15、區塊值介於0~3…
    Serial.println(F("Wrong sector or block number."));
    return;
  }

  byte blockNum = _sector * 4 + _block;  // 計算區塊的實際編號(0~63)
  byte trailerBlock = _sector * 4 + 3;   // 控制區塊編號

  // 驗證金鑰
  status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));
  // 若未通過驗證…
  if (status != MFRC522::STATUS_OK) {
    // 顯示錯誤訊息
    Serial.print(F("PCD_Authenticate() failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return;
  }

  byte buffersize = 18;
  status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockNum, _blockData, &buffersize);

  // 若讀取不成功…
  if (status != MFRC522::STATUS_OK) {
    // 顯示錯誤訊息
    Serial.print(F("MIFARE_read() failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return;
  }

  // 顯示「讀取成功!」
  Serial.println(F("Data was read."));

}
//===========================================================
void onMqttMessage(int messageSize) {
  // we received a message, print out the topic and contents
  Serial.print("Received a message with topic '");
  Serial.print(mqttClient.messageTopic());
  String Topic= mqttClient.messageTopic();
  Serial.print("', duplicate = ");
  Serial.print(mqttClient.messageDup() ? "true" : "false");
  Serial.print(", QoS = ");
  Serial.print(mqttClient.messageQoS());
  Serial.print(", retained = ");
  Serial.print(mqttClient.messageRetain() ? "true" : "false");
  Serial.print("', length ");
  Serial.print(messageSize);
  Serial.println(" bytes:");
  String topicIN="";
  // use the Stream interface to print the contents
  while (mqttClient.available()) {
    //Serial.print((char)mqttClient.read());
    topicIN=topicIN+(char)mqttClient.read();

  }

  Serial.println(topicIN);
  topicIN.trim();

  String  sec1 ,blk1 ,text1;
  sec1=topicIN.substring(0,2);
  blk1=topicIN.substring(3,5);
  text1=topicIN.substring(6);
  sector = sec1.toInt();
  block  = blk1.toInt();


  if (Topic.substring(14,18) == "read") {
    sec1=topicIN.substring(0,2);
    blk1=topicIN.substring(3,5);
    //String  text1=message.subString(6);
    sector = sec1.toInt();
    block  = blk1.toInt();  

 
    RD_flag=true;
    Serial.print ("Topic: ");    
    Serial.println(Topic);
    Serial.print ("Sector: ");
    Serial.print(sec1);
    Serial.print ("  Block: ");
    Serial.print(blk1);
    Serial.println("----------onMqttMessage----------");
  }
}
//===========================================================
//副程式  setup wifi
void setup_wifi() {
  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);     //print ssid
  WiFi.begin(ssid, pass);  //初始化WiFi 函式庫並回傳目前的網路狀態
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }   //假設 wifi 未連接 show ………

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}  
//===========================================================
void dump_byte_array(byte *buffer, byte bufferSize) {
  for (byte i = 0; i < bufferSize; i++) {
    Serial.print(buffer[i] < 0x10 ? " 0" : " ");
    Serial.print(buffer[i], HEX);
  }
}
//===========================================================
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;
}
//===========================================================
void setup() {
  pinMode(LED, OUTPUT);     // 設置板上LED引腳為輸出模式
  digitalWrite(LED, HIGH);  // 啟動後關閉板上LED
  //Initialize serial and wait for port to open:
  Serial.begin(115200);
  // Connecting to a WiFi network
  setup_wifi();
  Serial.println("You're connected to the network");
  Serial.println();

  // You can provide a unique client ID, if not set the library uses Arduino-millis()
  // Each client must have a unique client ID
  // mqttClient.setId("clientId");

  // You can provide a username and password for authentication
  // mqttClient.setUsernamePassword("username", "password");

  // By default the library connects with the "clean session" flag set,
  // you can disable this behaviour by using
  // mqttClient.setCleanSession(false);

  // set a will message, used by the broker when the connection dies unexpectedly
  // you must know the size of the message beforehand, and it must be set before connecting
  String willPayload = "oh no!";
  bool willRetain = true;
  int willQos = 1;

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

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

  if (!mqttClient.connect(broker, port)) {
    Serial.print("MQTT connection failed! Error code = ");
    Serial.println(mqttClient.connectError());

    while (1);
  }

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

  // set the message receive callback
  mqttClient.onMessage(onMqttMessage);

  Serial.print("Subscribing to topic: ");
  Serial.println(inTopic);
  Serial.println(topic1);
 
  Serial.println();

  // subscribe to a topic
  // the second parameter sets the QoS of the subscription,
  // the the library supports subscribing at QoS 0, 1, or 2
  int subscribeQos = 1;

  mqttClient.subscribe(inTopic, subscribeQos);
  mqttClient.subscribe(topic1, subscribeQos);

  // topics can be unsubscribed using:
  // mqttClient.unsubscribe(inTopic);

  Serial.print("Waiting for messages on topic: ");
  Serial.println(inTopic);
  Serial.println();
  //啟動 mfrc522
  SPI.begin();
  mfrc522.PCD_Init();   // 初始化MFRC522讀卡機模組
  mfrc522.PCD_DumpVersionToSerial();  // Show details of PCD - MFRC522 Card Reader details
  Serial.println(F("Please scan MIFARE Classic card..."));
  // 準備金鑰(用於key A和key B),出廠預設為6組 0xFF。
  for (byte i = 0; i < 6; i++) {
    key.keyByte[i] = 0xFF;
  }
}
//===========================================================
void loop() {
  // call poll() regularly to allow the library to receive MQTT messages and
  // send MQTT keep alives which avoids being disconnected by the broker
  mqttClient.poll();

 if (RD_flag==true) {
    String sect1 = String(sector);
    String bloc1  = String(block);
    Serial.print("sector=");
    Serial.print(sect1);
    Serial.print(", block=");
    Serial.println(bloc1);
    Serial.println("RD_flag=true");
  if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
  //if (mfrc522.PICC_ReadCardSerial()) {
    if ((sector>0) || (block>0)) {

      Serial.println("Reading RFID block");
      readBlock(sector, block, bufferarray);      // 區段編號、區塊編號、存放讀取資料的陣列

      Serial.print(F("Read block: "));        // 顯示儲存讀取資料的陣列元素值
      ///String str=String((char *)bufferarray);
      String str="";
      for (byte i = 0 ; i <16 ; i++) {      //顯示儲存讀取資料的陣列元素值
        Serial.write (bufferarray[i]);
        str+=(bufferarray[i]);
      }

      Serial.println(str);
      //========publish topic3 =========
      json = "sector=";
      json = json + String(sector);
      json = json + ", block=";
      json = json + String(block);
      json = json + ", Read data : ";
      //str=String((char *)bufferarray);
      json =json + str;
      json.trim();
 
      bool retained = false;
      int qos = 1;
      bool dup = false;

      mqttClient.beginMessage(topic3,  json.length(), retained, qos, dup);
      mqttClient.print(json);
      mqttClient.endMessage();
     
    }
    else
    {
      Serial.println(F("Please scan MIFARE Classic card..."));
      // 確認是否有新卡片
   
      byte *id = mfrc522.uid.uidByte;   // 取得卡片的UID
      byte idSize = mfrc522.uid.size;   // 取得UID的長度
      String Type;
      Serial.print("PICC type: ");      // 顯示卡片類型
      // 根據卡片回應的SAK值(mfrc522.uid.sak)判斷卡片類型
      MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak);
      Type= mfrc522.PICC_GetTypeName(piccType);
      Serial.println(mfrc522.PICC_GetTypeName(piccType));

      Serial.print("UID Size: ");       // 顯示卡片的UID長度值
      Serial.println(idSize);
 
      for (byte i = 0; i < idSize; i++) {  // 逐一顯示UID碼
        Serial.print("id[");
        Serial.print(i);
        Serial.print("]: ");
        Serial.println(id[i], HEX);       // 以16進位顯示UID值
      }
      Serial.println();



      json="";
      json = json +(" Card UID: ");
      String json1=printHex(mfrc522.uid.uidByte, mfrc522.uid.size);
      json1.toUpperCase();
      json = json + json1;
      json.trim();
      json = json +(" , PICC type: ");
      json =  json + Type;
      json.trim();

      bool retained = false;
      int qos = 1;
      bool dup = false;

      mqttClient.beginMessage(topic3,  json.length(), retained, qos, dup);
      mqttClient.print(json);
      mqttClient.endMessage();

      Serial.println();
      // Dump debug info about the card; PICC_HaltA() is automatically called
      mfrc522.PICC_DumpToSerial(&(mfrc522.uid));
    }

  RD_flag=false;
  Serial.println("RD_flag=false");
  // 令卡片進入停止狀態
  mfrc522.PICC_HaltA();
  mfrc522.PCD_StopCrypto1(); // stop encryption on PCD  
  } //if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
  } //if (RD_flag==true)
}
//===========================================================








[{"id":"b4fb005ddd98cabd","type":"function","z":"2f5520a7ea5f3ad2","name":"function  ","func":"var arr = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];\nmsg.payload=arr;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":300,"y":260,"wires":[["072d1871cda8fe10","b7314e689be39f11"]]},{"id":"241a1221cb70b19a","type":"inject","z":"2f5520a7ea5f3ad2","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":"1","topic":"","payload":"","payloadType":"date","x":160,"y":260,"wires":[["b4fb005ddd98cabd"]]},{"id":"072d1871cda8fe10","type":"debug","z":"2f5520a7ea5f3ad2","name":"debug 257","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":470,"y":260,"wires":[]},{"id":"b7314e689be39f11","type":"change","z":"2f5520a7ea5f3ad2","name":"","rules":[{"t":"set","p":"options","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":470,"y":220,"wires":[["9c81ed8345cfbe3a"]]},{"id":"9c81ed8345cfbe3a","type":"ui_dropdown","z":"2f5520a7ea5f3ad2","name":"","label":"SECTOR NO:","tooltip":"","place":"Select option","group":"eebf0fc66c6a4d88","order":1,"width":5,"height":1,"passthru":true,"multiple":false,"options":[{"label":"","value":"","type":"str"}],"payload":"","topic":"topic","topicType":"msg","className":"","x":660,"y":220,"wires":[["91390ae2119ac18a","84531c8ecba4f326"]]},{"id":"91390ae2119ac18a","type":"debug","z":"2f5520a7ea5f3ad2","name":"debug 258","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":810,"y":280,"wires":[]},{"id":"9ae822e79055e3aa","type":"inject","z":"2f5520a7ea5f3ad2","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":"1","topic":"","payload":"","payloadType":"date","x":160,"y":320,"wires":[["f51c807150d82db5"]]},{"id":"f51c807150d82db5","type":"function","z":"2f5520a7ea5f3ad2","name":"function  ","func":"var arr = [0,1,2];\nmsg.payload=arr;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":300,"y":320,"wires":[["5dcad4e96e4a29ea","beede6ee35ace32e"]]},{"id":"beede6ee35ace32e","type":"change","z":"2f5520a7ea5f3ad2","name":"","rules":[{"t":"set","p":"options","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":470,"y":360,"wires":[["542cf166f860474d"]]},{"id":"5dcad4e96e4a29ea","type":"debug","z":"2f5520a7ea5f3ad2","name":"debug 259","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":470,"y":320,"wires":[]},{"id":"542cf166f860474d","type":"ui_dropdown","z":"2f5520a7ea5f3ad2","name":"","label":"BLOCK    NO:","tooltip":"","place":"Select option","group":"eebf0fc66c6a4d88","order":3,"width":5,"height":1,"passthru":true,"multiple":false,"options":[{"label":"","value":"","type":"str"}],"payload":"","topic":"topic","topicType":"msg","className":"","x":650,"y":360,"wires":[["8afc0b1d53588863","376a1f319d2a04f8"]]},{"id":"8afc0b1d53588863","type":"debug","z":"2f5520a7ea5f3ad2","name":"debug 260","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":810,"y":420,"wires":[]},{"id":"ca7a327294243506","type":"ui_button","z":"2f5520a7ea5f3ad2","name":"","group":"eebf0fc66c6a4d88","order":5,"width":5,"height":1,"passthru":false,"label":"讀取","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"","payloadType":"str","topic":"topic","topicType":"msg","x":130,"y":60,"wires":[["5aa783b73c232a7f"]]},{"id":"4b626c8ff4fa0f66","type":"ui_text","z":"2f5520a7ea5f3ad2","group":"eebf0fc66c6a4d88","order":12,"width":"10","height":1,"name":"","label":"ESP回來字串","format":"{{msg.payload}}","layout":"row-spread","className":"","x":380,"y":480,"wires":[]},{"id":"5aa783b73c232a7f","type":"function","z":"2f5520a7ea5f3ad2","name":"function  ","func":"var par1 = context.global.sec_no ;\nvar par2 = context.global.blk_no ;\nif(par1<10)\n{\n   par1 = '0'+par1;\n}\nif(par2<10)\n{\n   par2 = '0'+par2;\n}\nvar par3= par1+','+par2;\n\nmsg.payload=par3;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":280,"y":60,"wires":[["aa4676b62203ae9a","bff0898877976165"]]},{"id":"84531c8ecba4f326","type":"function","z":"2f5520a7ea5f3ad2","name":"function  ","func":"context.global.sec_no=msg.payload;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":840,"y":220,"wires":[[]]},{"id":"376a1f319d2a04f8","type":"function","z":"2f5520a7ea5f3ad2","name":"function  ","func":"context.global.blk_no=msg.payload;\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":820,"y":360,"wires":[[]]},{"id":"aa4676b62203ae9a","type":"mqtt out","z":"2f5520a7ea5f3ad2","name":"","topic":"alex9ufo/RFID/read","qos":"1","retain":"false","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"841df58d.ee5e98","x":470,"y":60,"wires":[]},{"id":"bd8b284c8c0b817f","type":"mqtt in","z":"2f5520a7ea5f3ad2","name":"","topic":"alex9ufo/RFID/back","qos":"1","datatype":"auto-detect","broker":"841df58d.ee5e98","nl":false,"rap":true,"rh":0,"inputs":0,"x":170,"y":480,"wires":[["4b626c8ff4fa0f66"]]},{"id":"bff0898877976165","type":"debug","z":"2f5520a7ea5f3ad2","name":"debug 261","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":450,"y":120,"wires":[]},{"id":"e8550dbdd8bd1dca","type":"mqtt in","z":"2f5520a7ea5f3ad2","name":"","topic":"alex9ufo/RFID/read","qos":"1","datatype":"auto-detect","broker":"841df58d.ee5e98","nl":false,"rap":true,"rh":0,"inputs":0,"x":170,"y":420,"wires":[["f14e98e117c6bff9"]]},{"id":"f14e98e117c6bff9","type":"ui_text","z":"2f5520a7ea5f3ad2","group":"eebf0fc66c6a4d88","order":10,"width":"10","height":1,"name":"","label":"MQTT 發行的字串","format":"{{msg.payload}}","layout":"row-left","className":"","x":390,"y":420,"wires":[]},{"id":"eebf0fc66c6a4d88","type":"ui_group","name":"RFID  SECTOR  BLOCK READ","tab":"048debf3dd7e9641","order":2,"disp":true,"width":"10","collapse":false,"className":""},{"id":"841df58d.ee5e98","type":"mqtt-broker","name":"","broker":"broker.hivemq.com","port":"1883","clientid":"","autoConnect":true,"usetls":false,"compatmode":false,"protocolVersion":"4","keepalive":"15","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","birthMsg":{},"closeTopic":"","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willPayload":"","willMsg":{},"userProps":"","sessionExpiry":""},{"id":"048debf3dd7e9641","type":"ui_tab","name":"2024 RFID ","icon":"dashboard","disabled":false,"hidden":false}]

沒有留言:

張貼留言

Messaging API作為替代方案

  LINE超好用功能要沒了!LINE Notify明年3月底終止服務,有什麼替代方案? LINE Notify將於2025年3月31日結束服務,官方建議改用Messaging API作為替代方案。 //CHANNEL_ACCESS_TOKEN = 'Messaging ...