2020年10月12日 星期一

六、6-6 RFID/MFRC522 電子標籤UID讀取實習


在Arduino的世界中有一款型號為MFRC522的模組可用來讀取一些RFID電子標籤尤其是符合IOS14444A或是稱為Mifare規範的電子標籤而這一種規範的電子標籤遍佈在我們的身邊例如常見的優遊卡、一卡通、智慧信用卡、門禁卡或是學生證等等都是屬於這類的系統有興趣多了解一些這類的RFID的讀者可到下面筆者的部落格去看一看應該會有一個更清楚的概念。

照片三_2

圖六、6-6_1 各式IOS14444A/Mifare規範電子標籤


在本實習中會示範如何使用ESP8266連接MFRC522這塊RFID讀寫器模組並讓ESP8266以伺服器的腳色供使用者用手機等行動通信裝置連線上後在螢幕上顯示MFRC522所讀取到的RFID電子標籤UID號碼。

RFID相關知識部落格網址:

https://htcarduino.blogspot.com/2020/06/rfid.html


功能與動作說明

1、以ESP8266建立一無線WiFi AP存取點(即Soft--AP模式),這個AP存取點的SSID名稱為『ESP_softAP01』,而且不使用連線密碼。

2、此AP存取點內建伺服器的IP位址為:[ 192.168.4.1 ]

3、當有客戶端(Client手機、平板或筆電)裝置以瀏覽器連線上此伺服器時,會將接在ESP8266上的MFRC522這塊RFID讀寫器模組所讀取到的RFID電子標籤UID號碼顯示在手機瀏覽器的螢幕上。

4如果客戶端裝置持續的連線則系統會以固定的時間(在此為3秒)持續測試及讀取RFID電子標籤的UID號碼並主動傳送給客戶端裝置


圖六、6-6_2  MFRC522/RFID讀寫器模組與客戶端瀏覽器連線後預期畫面


  上面的【圖六、6-6_2】是本範例程式在客戶端瀏覽器連線後預期出現的畫面,畫面的左半部是正常讀取到電子標籤或卡片的結果,一般會讀取到4個位元組的UID標籤序號值,不過目前也有一些卡片的UID是6個位元組。而右半部的內容則是讀取失敗或是沒有診測到卡片的存在,如果同一張卡片一直放在RC522模組板上也會得到同樣的結果,同樣的卡片必須拿開後再次靠近才能再次讀取到卡片的UID值,。


電路圖

圖六、6-6_3 MFRC522與NodeMcu SPI匯流排介面電路圖

圖六、6-6_4  MFRC522與WeMos D1/R2 SPI匯流排介面電路圖


上面的【圖六、6-6_3】與【圖六、6-6_4】分別是MFRC522與NodeMcu 及WeMos D1/R2 SPI匯流排介面接線的電路圖讀者可依自己所使用的ESP8266模組種類去連線MFRC522/RFID讀寫器模組使用的是所謂的SPI匯流排介面它與ESP8266模組電路連接方式如下表:


/* wiring the MFRC522 to ESP8266 (ESP-12/E)

【RC522】 【ESP8266】

RST     =  GPIO5

SDA(SS) =  GPIO4 

MOSI    =  GPIO13

MISO    =  GPIO12

SCK     =  GPIO14

GND     =  GND

3.3V    =  3.3V


要注意的是這塊模組的工作電壓為3.3V,如果接到5V便可能有燒毀之虞!至於NodeMcu上腳位與標準ESP8266的對應則如下所示:


【NodeMcu】  【ESP8266】

D1     =  GPIO5(RST)

D2   =  GPIO4 (SS)

D7     =  GPIO13(MOSI)

D6     =  GPIO12(MISO)

D5     =  GPIO14(SCK)


程式列表與說明

  1. #include <ESP8266WiFi.h>

  2. #include "index.h"

  3.  

  4. #include <SPI.h>

  5. #include "MFRC522.h"

  6.  

  7. /* wiring the MFRC522 to ESP8266 (ESP-12)

  8. RST     = GPIO5

  9. SDA(SS) = GPIO4 

  10. MOSI    = GPIO13

  11. MISO    = GPIO12

  12. SCK     = GPIO14

  13. GND     = GND

  14. 3.3V    = 3.3V

  15. */

  16.  

  17. #define RST_PIN  5  // RST-PIN für RC522 - RFID - SPI - Modul GPIO5 

  18. #define SS_PIN  4  // SDA-PIN für RC522 - RFID - SPI - Modul GPIO4 

  19.  

  20. MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance

  21.  

  22. String softSsid = "ESP_softAP01";

  23. //String softSsid = "ESP"+String(ESP.getChipId());

  24. const char* softPassword = "12345678";

  25.  

  26. WiFiServer server(80);

  27.  

  28. String tmpString = "";    // html網頁程式字串變數

  29. unsigned int count = 0;   // 測輛次數值

  30. int rTime=3;              // 網頁更新時間

  31. String uid=""; 

  32.  

  33.   // 初始化程式區setup()開始 :

  34. void setup() 

  35. {

  36.     Serial.begin(115200);

  37.     Serial.println();

  38.  

  39.   Serial.println("Setting soft-AP Mode... ");

  40.   pinMode(2,OUTPUT);

  41.  

  42.   boolean result=WiFi.softAP(softSsid);

  43.   if(result==true)

  44.   {

  45.     Serial.println("soft-AP ready!");

  46.     Serial.print("softAP = ");

  47.     Serial.println(softSsid);

  48.     Serial.print("softIP = ");

  49.     Serial.println(WiFi.softAPIP());

  50.   server.begin();

  51.   }  

  52.   else

  53.     Serial.println("soft-AP failed"); 

  54.   

  55.   SPI.begin();           // Init SPI bus

  56.   mfrc522.PCD_Init();    // Init MFRC522

  57.   Serial.println("Ready!");

  58.   Serial.println("======================================================"); 

  59.   Serial.println("Scan for Card and print UID:");

  60.     digitalWrite(2,1);

  61. } // setup()結束

  62.  

  63. // 主迴圈 loop()開始 :

  64. void loop() 

  65. {

  66.     // Check if a client has connected

  67.     WiFiClient client = server.available();

  68.     if (!client)    

  69.       return;  

  70.   count++;

  71.     tmpString = MAIN_page;    // 取出html網頁回應程式

  72.     tmpString.replace("%rTime%", String(rTime) ); // 帶入測量的時間值到網頁html程式中

  73.   digitalWrite(2,0);

  74.   // Look for new cards

  75.     uid="未偵測到卡片!";

  76.   for(int i=0;i<(rTime/0.05);i++)   // 依照網頁更新的時間來決定是否偵測到卡片

  77.   {

  78.     if ( mfrc522.PICC_IsNewCardPresent())

  79.     {

  80.       if ( ! mfrc522.PICC_ReadCardSerial()) 

  81.         Serial.println("Read DIU Error!! ");

  82.       else

  83.       {

  84.   // Select one of the cards

  85.   // Show some details of the PICC (that is: the tag/card)

  86.          Serial.print("Card UID:");

  87.         dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size);

  88.       }

  89.   mfrc522.PICC_HaltA(); // Halt PICC

  90.   mfrc522.PCD_StopCrypto1();  // Stop encryption on PCD

  91.     }

  92.     delay(50);

  93.   }

  94.  

  95.     tmpString.replace("%uid%", String(uid) );    // 帶入測量的UID到網頁html程式中

  96.     tmpString.replace("%count%", String(count) ); // 帶入測量次數

  97.  

  98.     client.flush();

  99.     client.print( tmpString );

  100.       

  101.     digitalWrite(2,1);

  102.     delay(500);

  103. } // loop() 結束

  104.  

  105. // 將偵測到的卡片UID轉換至網頁變數(uid)內並顯示在Arduino IDE的監控視窗:

  106. void dump_byte_array(byte *buffer, byte bufferSize) {

  107.   uid="";

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

  109.     if(buffer[i] < 0x10)      // 如果卡號小於16(0f)在前面補0

  110.       uid+=" 0";

  111.     else

  112.       uid+=" ";

  113.     uid+=String(buffer[i],HEX);

  114.   }

  115.     Serial.println(uid);

  116. }

  117.  

  118. const char MAIN_page[] PROGMEM = R"=====(

  119. HTTP/1.1 200 OK

  120. Content-Type:text/html

  121. Connection: close

  122.  

  123. <!DOCTYPE html>

  124. <html>

  125.  <head>

  126.  <meta name='viewport' content='width=device-width, initial-scale=1.0'/>

  127.  <meta charset='utf-8'>

  128.  <meta http-equiv='refresh' content='%rTime%'>

  129.  <style>

  130.    body {font-size:100%;} 

  131.    #main {display: table; margin: auto;  padding: 0 10px 0 10px; } 

  132.  </style>

  133.    <title>Soft AP模式-RFID/RC522電子標籤UID讀取</title>

  134.  </head>

  135.  

  136.  <body> 

  137.    <div id='main'>

  138.      <h2><center>[Soft AP模式] <br>

  139.          RFID/RC522電子標籤UID讀取實習<br></center><br>

  140.        卡片UID序號 =  %uid% <br>

  141.        Card UID =  %uid%<br>

  142.       Count = %count%<br>

  143.       </h2>

  144.    </div> 

  145.  </body>

  146. </html>

  147. )=====";

程式名稱:ESP_CH6_6_6_RC522.ino


在本範例的程式中會引用到兩個外部的函式庫分別是<SPI.h>及"MFRC522.h"其中的<SPI.h>是Arduino IDE內建的程式庫因此可以直接引用沒有問題但是因為「MFRC522這塊模組板並不是Arduino IDE中的標準內建元件,所以必須另外加裝相關的函示庫之後才能引用。上面程式中第45行便是宣告引用了<SPI.h>與"MFRC522.h"這兩個函式庫。


4. #include  <SPI.h>    // 引入<SPI.h>函式庫

5. #include "MFRC522.h"  // 引入"MFRC522.h"函式庫


要按裝"MFRC522.h"這個函示庫請先在Arduino IDE中點選上方的【草稿碼】🡺【匯入程式庫】🡺【管理程式庫】,在看到下面的【圖六、6-6_5】程式庫管理員視窗後在標記1的欄位輸入『MFRC522』搜尋字串便可以找到標記2的目標函式庫然後在標記3的地方選擇所需的版本(一般都會選擇最新的版本)並按下[安裝]鍵便可成功安裝如果系統已經安裝過的話就會在標記2中看到「INSTALLED」這樣的訊息文字。


圖六、6-6_5 MFRC522.h函式庫安裝畫面


由於MFRC522使用的是SPI匯流排介面,如果沒有特別指定的話在初始化時會使用ESP8266內建的MISO(GPI12)、MOSI(GPIO13)及SCK(GPIO14)這3根接腳,不過RST與SS這兩根腳必須另外宣告使用的腳位


17. #define  RST_PIN  5  // RST-PIN RC522 - RFID  SPI – ESP/ GPIO5 

18. #define  SS_PIN  4  // SDA-PIN RC522 – RFID  SPI – ESP/ GPIO4 


本範例程式的初始化程式區(setup())內容和前面幾個實習非常類似差別只在下面這兩行用來初始化SPI匯流排與MFRC522的指令而已


55.       SPI.begin();           // Init SPI bus

56.   mfrc522.PCD_Init();    // Init MFRC522


在主迴圈(loop())程式中一開始同樣是在等待客戶端連線(67~69行),當有客戶端裝置連線時會先把連線次數(count)加一,並把網頁首頁程式碼MAIN_page[]傳送到「tmpString這個字串變數中同時也把測量的時間間隔值(即'rTime')帶入到網頁html程式中:


70.   count++;

71.    tmpString = MAIN_page;    // 取出html網頁回應程式

72.    tmpString.replace("%rTime%", String(rTime) ); 


為了配合網頁自動更新的時間(在此為3秒)因此我們利用76~93行的for()迴圈流程控制方式去測試在此特定的時間內是否有卡片出現在MFRC522模組的感應範圍內不過在此之前我們先預設回傳的訊息為沒有偵測到:


75.    uid="未偵測到卡片!";


接著使用下列指令測試是否有新的電子標籤或卡片出現在MFRC522模組的感應範圍內


78.      if ( mfrc522.PICC_IsNewCardPresent())


如果有便開始讀取該卡片的UID號碼,假如讀取失敗會將訊息「"Read DIU Error!! "顯示在Arduino IDE的序列監控視窗中反之則以16進制的形式列印出卡片的UID號碼而卡片UID號碼真正的顯示與轉換是由『dump_byte_array()這個副程式負責:


79.    {

80.      if ( ! mfrc522.PICC_ReadCardSerial()) 

81.        Serial.println("Read DIU Error!! ");

82.      else      

83. {

84.   // Select one of the cards

85.   // Show some details of the PICC (that is: the tag/card)

86.         Serial.print("Card UID:");

87.        dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size);

88.      }


MFRC522模組在操作時不管是否讀取成功最後都必須停止感應與讀取的動作以便可以正確的處理下一次程序;如果不關閉動作在讀取成功後假如卡片沒有移開則會不斷的送出同一張卡片的UID號碼而造成困擾。下面的程式便是用來停止MFRC522模組動作之用:


89.  mfrc522.PICC_HaltA(); // Halt PICC

90.  mfrc522.PCD_StopCrypto1();  // Stop encryption on PCD


至於95、96行程式會把『dump_byte_array()』這個副程式轉換的卡片UID結果(即變數”uid”),與量測次數值(count)分別帶入「tmpString」這個字串變數中的「uid」、與「count」這兩個變數中


95. tmpString.replace("%uid%", String(uid) ); //帶入測量的UID到網頁html程式中

96. tmpString.replace("%count%", String(count) ); // 帶入測量次數


最後執行下列程式,便可以把MFRC522模組量測到的結果回應給連線的客戶端瀏覽器。


99.    client.println( tmpString );  


106~116 行程式是『void dump_byte_array(byte *buffer, byte bufferSize)這個副程式的主體部分這個副程式會將將偵測到的卡片UID號碼轉換至網頁字串變數(uid)並顯示在Arduino IDE的監控視窗中;此外為了美觀起見如果該號碼位元組(byte)的值小於16時會在前面補一個字元’0’以便每個位元組都是以兩位數16進制的方式呈現出來。


圖六、6-6_6  index.h程式頁面畫面


程式列表的118~147行是ESP8266內建伺服器的網頁首頁程式碼,它們是放在Arduino IDE程式中的「index.h」分頁上(如上圖所示),整個html程式的字串變數名稱跟之前一樣命名為「MAIN_page[]」為了能讓客戶端的瀏覽器能更快的取得MFRC522讀取電子標籤的結果在這裡一樣把更新時間加快到3秒鐘為了能讓程式更有彈性所以在此是以參數(即'%rTime%')的方式去定義


128. <meta http-equiv='refresh' content='%rTime%'>


【圖六、6-6_2】的中標記1也就是本實習名稱的提示訊息部分是由下面兩行程式完成:


138.     <h2><center>[Soft AP模式] <br>

139.         RFID/RC522電子標籤UID讀取實習<br></center><br>


網頁中MFRC522讀取電子標籤的結果也就是UID值,則是由下面幾行程式負責帶入網頁程式中,而這兩個參數(即「uid」與「count」)的傳遞是由前面介紹的主程式95、96行程式所負責:


140.      卡片UID序號 =  %uid% <br>

141.      Card UID =  %uid%<br>

142      Count = %count%<br>


至於「count」同樣代表網頁更新的次數,也等同於MFRC522讀取電子標籤的次數。


執行結果:

圖六、6-6_7  MFRC522讀取各種電子標籤UID畫面


【圖六、6-6_7】是使用者客戶端(如手機)瀏覽器與系統連線後系統上的MFRC522模組測量周邊是否有電子標籤或卡片接近後所回傳給瀏覽器的畫面,畫面左邊三個頁面是MFRC522模組偵測並讀取到三個不同標籤的UID號碼畫面,而最右邊則是在網頁更新的時間(3秒)內沒有偵測到任何卡片出現或是上一次測到的卡片沒有移開仍然在MFRC522模組偵測的範圍內時所出現的訊息。

本實習主要是示範ESP8266如何與MFRC522模組連接進而讓使用者可以透過WiFi無線傳輸的方式直接與行動通訊裝置連線當這些通訊連線的基本問題都解決之後我們可以讓ESP6266從存取點(AP點)的腳色轉換為站點(Station)的腳色然後連接已經連上網際網路(Internet)的WiFi分享器(或Router)這樣便可以讓我們這MFRC522/RFID讀寫器裝置成為IOT物聯網的一份子至於這個系統能夠做那些應用就要靠讀者們自己動動腦發揮一下創意了!


沒有留言:

張貼留言