今天,我们将制作一款Arduino RFID门锁。我想要找到一种简单并且安全的方法来锁门,而不必购买一个昂贵的装置。接下来,我们将要学习射频识别(RFID)技术并将其应用到无线通信中。

什么是RFID?

“RFID表示射频识别,[…]RFID的作用与信用卡或者ATM卡背面的条形码或磁条相同;它为物体提供了唯一的标识符。而且,就像必须扫描条形码或词条才能获取信息一样,RFID设备必须经过扫描才能获取相关的识别信息。”– 摘自©Technovelgy,一个记录科学技术与科幻创意的网站。
在本项目中通过使用RFID技术来从RFID标签中读取数据,并将信息发送到MCU的非易失性存储器。从标签读取的ID与存储的信息进行比较,如果匹配,则门将被解锁/打开。

硬件

  • Arduino Nano
  • RFID RC522
  • 压电蜂鸣器
  • 2x LED
  • 2x 330 电阻
  • 4×4 键盘
  • LCD适配器I2C
  • LCD16X2BL

软件

  • Arduino IDE
  • SketchUp
  • Github

将LCD 连接到Arduino

LCD有16个引脚,对于Arduino Nano来说引脚数量过多,因此一定要使用I2C适配器,如此一来可以仅通过Arduino的两个信号引脚来实现显示功能。这很有帮助,因为这种情况下需要通过MCU控制的引脚数量就会很少。
2016-09-19_RFID-Door-Lock-fig1.jpg
图1:LCD引脚

LCD具有并行接口,这意味着MCU必须同时操控多个接口引脚来对显示进行控制。下表提供了每个引脚的说明:
2016-09-19_RFID-Door-Lock-fig2.jpg
图2:引脚规格

首先,我们将在LCD和I2C之间建立连接。为此,我们需要一个I2C LCD显示适配器(LCD1602)。该适配器将16 x 2字符LCD显示转换为串行I2C LCD,仅用两根导线连接即可通过Arduino进行控制。

2016-09-19_RFID-Door-Lock-fig3.jpg
图3:Arduino和LCD之间的连接

接下来,我们将在Arduino IDE中添加库“<LiquidCrystal_I2C.h>”。我们可以通过该库将LCD连接到Arduino。内置的LiquidCrystal_I2C库可以轻松实现在LCD屏幕上显示字母。

您可以点击此处下载LiquidCrystal_I2C.h

请按照以下步骤将新的库安装到您的Arduino IDE中:

1) 首先,从Github上下载文件(我将要下载的是Keypad库)。
2016-09-19_RFID-Door-Lock-step1.jpg
2) 压缩(.zip)每个文件夹。
2016-09-19_RFID-Door-Lock-step2.jpg
3) 拷贝.zip文件到您的Arduino文件夹中。
2016-09-19_RFID-Door-Lock-fig6.jpg
4) 打开Arduino并添加Keypad.zip::Sketch menu > Include Library > Add .ZIP Library。
2016-09-19_RFID-Door-Lock-fig7.jpg
5) 添加keypad库:Sketch menu > Include Library > Keypad.
2016-09-19_RFID-Door-Lock-fig8.jpg

测试LCD的代码:
  1. #include <Wire.h>
  2. #include <LiquidCrystal_I2C.h>
  3. LiquidCrystal_I2C lcd(0x27, 16, 2); // Set the LCD address to 0x27 for a 16 chars and 2 line display
  4. void setup() {
  5. lcd.begin();// initialize the LCD
  6. lcd.backlight();// Turn on the blacklight and print a message.
  7. lcd.backlight();
  8. lcd.print("Hello, world!");
  9. }
  10. void loop() {
  11. }
2016-09-19_RFID-Door-Lock-fig9.jpg
图4:连接到Arduino Nano的LCD

连接键盘


现在,要进入键盘部分了!我们将要对键盘进行连接,以实现在LCD上显示从键盘输入的数字。

Keypad.h 是一个让Arduino能够从键盘读取矩阵类型键盘数据的库。
在本教程中我使用的是一个 4×4 键盘。

下表显示了Arduino开发板与键盘之间的连接情况。键盘引脚连接到Arduino的数字输出引脚。D6引脚是PWM引脚,因此用于蜂鸣器。
Keypad pinArduino pin
1D2
2D3
3D4
4D5
5A0
6D7
7D8

2016-09-19_RFID-Door-Lock-fig11.jpg
图5:Arduino、LCD以及键盘之间的连接
2016-09-19_RFID-Door-Lock-fig12.jpg
图6:连接到Arduino的LCD和键盘

连接RFID


接下来,我们将添加RFID。在这种情况下,RFID板使用SPI通信协议,其中Arduino将作为主机,而RFID阅读器将作为从机。读卡器和标签将被设计为以13.56MHz大小的频率进行通信。
这是很重要的一步,因为RFID会帮助我们从卡上读取数据,并且确定ID是否对应于EEPROM中存储的信息。如果匹配,我们就能够进入房间并显示“解锁”。否则,LCD将显示“锁定”。

2016-09-19_RFID-Door-Lock-fig13.jpg
图7: Arduino、LCD以及RFID之间的连接
2016-09-19_RFID-Door-Lock-fig14.jpg
图8: Arduino、LCD以及RFID

添加蜂鸣器以及LED

下一步是添加一个蜂鸣器和2个LED,以模拟被控制访问系统的状况。请查看下面的接线图(图9)。我们设置了一个蜂鸣器,可以在获得访问权限(解锁)时蜂鸣。锁定时,红色LED始终亮起,解锁时,绿色LED会亮起。
为了保护这些设备模块,我决定用3D打印制造出收纳的盒子。如果您没有3D打印机,可以使用一个可以盛放所有组件的塑料盒。这会很有帮助,因为这样一来这些模块将会被放置于内部,只有LED、键盘和LCD会放置在盒子外。
2016-09-19_RFID-Door-Lock-fig15.jpg
图9:Nano、 LCD、键盘、RFID和蜂鸣器连接接线图

制造收纳的盒子

对于本项目,我想要制造一个自定义的盒子来收纳所有的组件,并保护这些组件不被损坏。
我使用SketchUp设计了一个盒子,这个软件具有用户友好型界面,有一些简单的操作按钮,如橡皮擦、线条和卷尺工具等。
盒子的尺寸是 120x125x37 mm。
2016-09-19_RFID-Door-Lock-fig16.jpg
如果您对Sketchup不熟悉,建议您点击此处阅读一些Sketchup教程。
2016-09-19_RFID-Door-Lock-fig17.jpg
图10:收纳盒子(顶部视图)
2016-09-19_RFID-Door-Lock-fig18.jpg
图11:收纳盒子(底部视图)

设计盒子时,我考虑了以下尺寸:

  • 顶部视图:

    • •        2 个用于LED的开口 (5.2 mm)
    • •        1 个用于LCD的开口 (42.2×7.3 mm)
    • •        1 个用于线缆的开口(16×10.5 mm)span>


  • 底部视图

    • •        1 个用于键盘的开口(27×10 mm)

在测量了组件的尺寸之后,我设计了一个非常紧凑的盒子。您可以根据自己的喜好对设计进行更改。
2016-09-19_RFID-Door-Lock-fig19.jpg
图12:内部装有模块的完整收纳盒


代码流程图 2016-09-19_RFID-Door-Lock-fig20.jpg
完整代码
  1. #include <SPI.h>
  2. #include <MFRC522.h>
  3. #include <Wire.h>
  4. #include <LiquidCrystal_I2C.h>
  5. #include <Keypad.h>
  6. #include<EEPROM.h>
  7. int relPin;
  8. int state=0;
  9. byte  COD[10];
  10. byte  AUX[10];
  11. int k=0;
  12. String accessCode="*123456#";
  13. String codpairing="*654321#";
  14. //NFC
  15. #define RST_PIN   9
  16. #define SS_PIN 10   
  17. MFRC522 mfrc522(SS_PIN, RST_PIN);   // Create MFRC522 instance
  18. #define NEW_UID {0xDE, 0xAD, 0xBE, 0xEF}
  19. MFRC522::MIFARE_Key key;
  20. //LCD
  21. LiquidCrystal_I2C  lcd(0x27,16,2);
  22. //KEYPAD
  23. const byte numRows= 4; //number of rows on the keypad
  24. const byte numCols= 4; //number of columns on the keypad
  25. char keymap[numRows][numCols]=
  26. {
  27. {'1', '2', '3', 'A'},
  28. {'4', '5', '6', 'B'},
  29. {'7', '8', '9', 'C'},
  30. {'*', '0', '#', 'D'}
  31. };
  32. //Code that shows the the keypad connections to the arduino terminals
  33. byte rowPins[numRows] = {2,3,4,5}; //Rows 0 to 3
  34. byte colPins[numCols]= {A0,7,8,9}; //Columns 0 to 3
  35. //initializes an instance of the Keypad class
  36. Keypad myKeypad= Keypad(makeKeymap(keymap), rowPins, colPins, numRows, numCols);
  37. void setup() {
  38.   pinMode(A0,OUTPUT);
  39.   digitalWrite(A0,HIGH);
  40.   pinMode(A3,OUTPUT);
  41.   digitalWrite(A3,HIGH);
  42.   pinMode(A1,OUTPUT);
  43.   digitalWrite(A1,HIGH);
  44.   pinMode(A2,OUTPUT);
  45.   digitalWrite(A2,LOW);
  46.   //NFC
  47.   Serial.begin(9600);  // Initialize serial communications with the PC
  48.   while (!Serial);  // Do nothing if no serial port is opened
  49.   SPI.begin(); // Init SPI bus
  50. mfrc522.PCD_Init();     // Init MFRC522 card
  51. for (byte i = 0; i < 6; i++) {        
  52. key.keyByte[i] = 0xFF;
  53. }
  54. lcd.begin();
  55. lcd.backlight();
  56. lcd.setCursor(0,0);
  57. lcd.clear();
  58. lcd.print( "BLOCKED" );
  59. }
  60. void  readNFC(){   // This function will read the code stored on  
  61. for (byte i =0; i<(mfrc522.uid.size); i++) {  // the  UID
  62.    COD[i]=mfrc522.uid.uidByte[i];
  63. }
  64. Serial.print("COD");
  65. Serial.print(COD[0]);
  66. Serial.print(COD[1]);
  67. Serial.print(COD[2]);
  68. Serial.print(COD[3]);
  69. }
  70. void pairNFC(){
  71. Serial.println("COD in pair");
  72. Serial.print(COD[0]);
  73. Serial.print(COD[1]);
  74. Serial.print(COD[2]);
  75. Serial.print(COD[3]);
  76.   long  r=0;
  77.   int c=0;
  78.   for(int i=1;i<=EEPROM.read(0);i++){                       //The UID cannot be stored on
  79. switch(i%4){                                                     // one variable, it was needed to be
  80.       case 1 :{AUX[0]=EEPROM.read(i); break;}     // split
  81.       case 2 :{AUX[1]=EEPROM.read(i); break;}
  82.       case 3 :{AUX[2]=EEPROM.read(i); break;}
  83.       case 0 :{AUX[3]=EEPROM.read(i); break;}
  84. }
  85. if((i)%4==0)
  86.    {Serial.println(r);
  87.     if( AUX[0]==COD[0] && AUX[1]==COD[1] && AUX[2]==COD[2] && AUX[3]==COD[3] ){                                      //Verify if the code is in EEPROM
  88.          lcd.clear();
  89.          lcd.setCursor(0,0);
  90.          lcd.print("CODE ALREADY IN");
  91.          lcd.setCursor(0,1);
  92.          lcd.print("SYSTEM");
  93.       delay(2000);
  94.           c=1;
  95.       break;}
  96.   }
  97.   }
  98. if(c==0){int aux2=EEPROM.read(0);
  99.              Serial.println("CODE PAIRED");
  100. Serial.print(COD[0]);
  101. Serial.print(COD[1]);
  102. Serial.print(COD[2]);
  103. Serial.print(COD[3]);
  104.          EEPROM.write(aux2+1,COD[0]);  //Writing code in EEPROM
  105.          EEPROM.write(aux2+2,COD[1]);
  106.          EEPROM.write(aux2+3,COD[2]);
  107.          EEPROM.write(aux2+4,COD[3]);
  108.          aux2=aux2+4; // Position for a new code
  109.          Serial.println("aux2");
  110.          Serial.println(aux2);
  111.          EEPROM.write(0,0);
  112.          EEPROM.write(0,aux2);   
  113.          lcd.clear();
  114.          lcd.setCursor(0,0);
  115.          lcd.print("CODE PAIRED");
  116.          delay(2000);}   
  117. }
  118. boolean validationNFC(){
  119.   boolean c=false;
  120.   for(int i=1;i<=EEPROM.read(0);i++){   //Read the EEPROM
  121.       switch(i%4){
  122.       case 1 :{AUX[0]=EEPROM.read(i); break;}
  123.       case 2 :{AUX[1]=EEPROM.read(i); break;}
  124.       case 3 :{AUX[2]=EEPROM.read(i); break;}
  125.       case 0 :{AUX[3]=EEPROM.read(i); break;}
  126.     }
  127. if((i)%4==0)
  128.    {
  129.     if( AUX[0]==COD[0] && AUX[1]==COD[1] && AUX[2]==COD[2] &&                AUX[3]==COD[3])
  130.       c=true; //Verify if the code is in EEPROM and make flag=true;
  131.      }
  132. }      
  133.   return c;  
  134. }
  135. int compareCODE(String a)    //We type a code on keypad and this will be compared
  136. { //with the accessCode;
  137. if(a.equals(accessCode))   
  138. return 1;
  139. else if(a.equals(codpairing)) return 2;
  140. else return 0;  
  141. }
  142. String takeCode(char x) //Will display on the LCD the code typed
  143. { char vec[10];
  144. vec[0]=x;
  145. lcd.setCursor(0,0);
  146. lcd.clear();
  147. lcd.print('X');
  148. for(int i=1;i<8;i++)
  149.     {vec[i]=myKeypad.waitForKey(); //Waits for 8 keys to be pressed and after that  
  150.      lcd.print('X');} //is taking the decision
  151. vec[8]=NULL;
  152. String str(vec);
  153. return str;
  154. }  
  155.   void loop() {
  156.    
  157.   switch(state){
  158.   case 0: {
  159.     mfrc522.PCD_Init();
  160.     if (  mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial() ){
  161.    
  162.     readNFC(); //It will read the card and it will search for UID in its
  163.     if(validationNFC()) //memory
  164.        { state=1;
  165.         lcd.clear();
  166.         lcd.setCursor(0,0);
  167. lcd.print( "VALID NFC CODE" ); //The door will be opened
  168.         delay(1000);
  169.         return;
  170.        }
  171.     else{
  172.          lcd.clear();
  173.          lcd.setCursor(0,0);
  174.          lcd.print( "INVALID NFC CODE" ) //If the code was wrongblocked
  175.          delay(1000);
  176.          lcd.setCursor(0,0);
  177.          lcd.clear();
  178.          lcd.print( "BLOCKED" );
  179.          return;
  180.         }
  181.     }      
  182. char c=myKeypad.getKey();
  183.   if(c != NO_KEY){
  184.    
  185. String codcurent=takeCode(c);
  186. int A=compareCODE(codcurent);
  187.   if(A==0){                                //A is a variable that stores the current code
  188.    lcd.clear();
  189.    lcd.print("INVALID CODE");
  190.    delay(2000);
  191.     lcd.setCursor(0,0);
  192.     lcd.clear();
  193.     lcd.print("BLOCKED");
  194.    return;
  195. }
  196.   if(A==1){
  197. lcd.setCursor(0,0);
  198.      lcd.clear();
  199.    lcd.print( "VALID CODE " );
  200. delay(2000);
  201.    state = 1;
  202.    Return;
  203. }
  204.   if(A==2); {
  205. state=2;
  206.          lcd.clear();
  207.          lcd.setCursor(0,0);
  208.         lcd.print( " Pairing..." );
  209.       delay(2000);
  210.     return;}
  211.   }
  212. break;
  213.    }
  214. case 1:{
  215.     lcd.clear();
  216.     lcd.setCursor(0,0);
  217.     lcd.print( "UNLOCKED" );
  218.     digitalWrite(A3,LOW);
  219.     digitalWrite(A1,LOW); //The red LED will be off
  220.     digitalWrite(A2,HIGH); //The green LED will be on
  221.     tone(6,3000,5010); //The buzzer will make a sound
  222.     delay(5000); //After 5 seconds the system will be blocked
  223.     digitalWrite(A3,HIGH);
  224.     digitalWrite(A1,HIGH);
  225.     digitalWrite(A2,LOW);
  226.     state=0;
  227.     lcd.setCursor(0,0);
  228.     lcd.clear();
  229.     lcd.print( "BLOCKED" );
  230. return;
  231.     }
  232.   case 2:{
  233.    mfrc522.PCD_Init();
  234.       if (  mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial() ){
  235.       readNFC();
  236.       pairNFC();
  237.       state=0;
  238.       delay(2000);
  239.       lcd.clear();
  240.       lcd.setCursor(0,0);
  241.       lcd.print( "BLOCKED" );   }
  242. break;
  243. }
  244. }
  245. }

结论

至此,我们成功制作了一款低成本的Arduino RFID门锁。对我来说,这是一个很有趣的项目,因为我是为自己使用而制作的。此外,制作一个这样的设备不仅能让您因为做了一些有用的事而产生满足感,学习到很多知识,还能让您对自制电子设备有初步的尝试和体验。与各种各样的开发板打交道是非常具有挑战性的。本项目还包含了很多有趣的课题,例如:

  • •        与其他Arduino库一起使用
  • •        理解EEPROM的使用
  • •        使用多种通信,如I2C, SPI
来源:techclass.rohm