[아두이노] RFID-RC522+Bluetooth+초음파 센서 실험

IOT/아두이노|2019. 5. 8. 09:00

[아두이노] RFID-RC522+Bluetooth+초음파 센서 실험



지난 시간에 RFID-RC522 + Servo Motor를 제어하는 실험을 하였습니다. 오늘은 몇가지 상황을 정하고 그 상황에 맞게 아두이노를 제어하는 실험을 하고자 합니다.

첫번째, 실험 내용은 RFID-RC522는 카드를 읽는 동작만 수행하는 아두이노와 Servo Motor를 회전 시키는 아두이노로 나누고 싶은 때가 있을 수 있습니다. 역할 분담으로 설계를 하고 싶을 때 그 문제를 해결 하기 위해서 두 아두이노 사이의 통신을 통해서 메세지를 주고 받으면 됩니다. 통신 중의 무선 통신 모듈인 Bluetooth 통신을 접목하여 실험을 하고 싶었습니다. 실제 실험의 내용은 Card가 저장된 UID랑 일치하면 Bluetooth를 통해 스마트폰에 메세지를 전송하는 과정을 실험 하였습니다. 이렇게 메세지를 전송할 수 있다면 다른 아두이노가 있으면 그 아두이노가 블루투스로 메세지를 수신하고 그에 따른 회전 명령을 수행하면 간단히 역할을 나눠서 동작할 수 있게 됩니다. 실제로 역할 분담 실험을 하기에는 부품이 부족하여 아쉽게 실험을 할 수 없고 해서 무선 통신으로 스마트폰에 메세지를 전송하는 데 까지만 실험을 해도 충분히 실험의 목적을 달성 할 수 있기 때문에 이정선에서 실험을 하기로 결정 했습니다.

두번째, 실험 내용은 RFID-RC522 리더기로 Card를 읽고 Servo Motor가 회전 되었을 때 다시 원위치로 되돌아 가게 하는 방법을 실험하고자 합니다. 실험에 쓰인 부품은 초음파센서 입니다. Servo Motor를 출입문이라고 가정하고 Card가 인식되면 180(Open)도 회전 후 사람이 출입문을 지나가고 나면 출입문을 통과 된 상황을 설정하면 자동으로 Servo Motor가 다시 0(Close)도 회전 되어 문이 닫히게 하면 좋을 것 같아서 상상해 보았습니다. 그 역할을 초음파센서를 이용할 예정입니다. 상상을 더하면 초음파센서가 인간이 지나기 전에 가지고 있는 거리측정값에서 인간이 지나가면 초음파센서의 거리측정값이 작아집니다. 인간를 감지한 값이 됩니다. 이때 초음파 센서값을 작아졌을 때 Servo Motor를 0(Close)도 회전 시켜서 문이 닫힌다는 설정입니다. 초음파센서의 위치는 안전위치에 문이 닫혀도 되는 위치에 설치 해야 겠지요.

1. RFID-RC522의 Bluetooth 통신



실험은 RFID-RC522 리더기로 Card를 인식하면 그 인식한 값을 Bluetooth를 이용하여 스마트폰에 전송하는 실험을 하겠습니다. 본 실험의 목적은 RFID-RC522 모듈을 사용하면서 이 모듈을 통해서 얻은 데이터를 외부 다른 기기로 전송하기 위해서 입니다. 지금 하고자 하는 실험은 Bluetooth 통신으로 외부로 기기(스마트폰)으로 RFID-RC522 리더기의 결과를 보낼 수 있다면 나중에 아두이노 두대로 나눠서 Bluetooth 통신으로 하나는 RFID-RC522 리더기 제어하고 하나는 Servo Motor제어를 하게 할 수 있게 됩니다.


1) RFID-RC522 + ServoMotor+피에조부저+Bluetooth 회로도



  • 준비물 : RFID-RC522, Servo Motor, 피에조부저, Bluetooth, 아두이노우노
  • 내용 : SPI 통신을 할 수 있게 핀은 연결하고 Servo Pin 7번, 피에조부저 6번으로 연결해 보자. 2,3번 핀을 Bluetooth핀으로 사용.
  • 지난시간 회로도 참고 : [아두이노] RFID-RC522 제어

  • BlueTX - arduinoRx - 2번
  • BlueRX - arduinoTx - 3번

출처 : Fritzing

지난 시간의 회로도에서 Bluetooth 모듈이 추가로 더 연결된 회로도 입니다. 2,3번 핀만 Bluetooth로 추가로 연결하시면 됩니다.

2) 코딩



MFRC522

#include <SPI.h>
#include <MFRC522.h>
  • MFRC522 mfrc522(SS_PIN, RST_PIN) : MFRC522 인스턴스화
  • SPI.begin() :SPI bus 초기화
  • mfrc522.PCD_Init() : MFRC522 card 초기화
  • mfrc522.PICC_IsNewCardPresent() : 새카드 확인
  • frc522.PICC_ReadCardSerial() : 하나의 카드읽기 확인
  • rfid.uid.uidByte[] : 읽은 카드 키값이 들어 있음

피에조 부저

  • tone(tonePin ,음,음길이) : 음 시작
  • noTone(tonePin ) : 음 중지

Servo Motor

#include <Servo.h>
  • Servo servo : 서보모터 객체 선언
  • servo.attach(서보핀) : 서보 모터에 출력을 담당할 핀 선언
  • servo.write(회전각) : 회전각 만큰 서보모터를 회전 시킴

시리얼 통신

  • Serial.begin(9600) : 시리얼 통신 시작(9600 통신속도)
  • Serial.println(값) : 시리얼모니터 출력
  • Serial.available() : 시리얼통신으로 데이터가 들어놨는지 상태 확인
  • Serial.parseInt() : Int형으로 데이터를 읽음

Bluetooth(SoftwareSerial 통신)

#include <SoftwareSerial.h>

SoftwareSerial mySerial (rx, tx) : 소프트시리얼 객체선언(rx(수신), tx(전송))
mySerial.begin(9600) : 시리얼 통신 시작(예로 9600 통식속도를 사용해 봤네요.)
mySerial.println(값) : 데이터 전송


지난 시간 소스에서 Bluetooth를 다음과 SfotwareSerial 명령들을 추가하면 됩니다. 간단히 Serial 부분을 SoftwareSerial 형태로 변경만 해주시면 됩니다.

SoftwareSerial mySerial (rx, tx) : 소프트시리얼 객체선언(rx(수신), tx(전송))

이렇게 통신객체변수 mySerial로 선언을 해주면 다음과 같이 표현만 해주시면 됩니다.

Serial.begint(9600) => mySerial.begin(9600)
Serial.println("Open") => mySerial.println("Open")
Serial.println("Close") => mySerial.println("Close")

이렇게 SoftwareSerial 통신을 하면 됩니다.

Bluetooth 부분을 추가하여 수정 하면 다음과 같습니다.

[소스]

#include <SPI.h>
#include <MFRC522.h>
#include <Servo.h>
#include <SoftwareSerial.h>

const int RST_PIN = 9;
const int SS_PIN = 10;

MFRC522 mfrc522(SS_PIN, RST_PIN);   // MFRC522 인스턴스

byte cardkeyByte[4] = {0x55, 0xAF, 0x07, 0x88}; //card UID 
boolean state = false; //Servo Motor 상태값

Servo servo;
const int SERVO_PIN = 7;
const int TONEPIN = 6;

const int rxPin = 2;
const int txPin = 3;
SoftwareSerial mySerial(rxPin, txPin); // RX, TX

void setup() {
  Serial.begin(9600);  
  while (!Serial);  
  mySerial.begin(9600);     
  SPI.begin();         //SPI 시작
  mfrc522.PCD_Init();  //MFRC522 card 초기화
  Serial.println(F("Warning: this example clears your mifare UID, use with care!"));

  servo.attach(SERVO_PIN);
  servo.write(0);
  delay(50);
}

void loop() {
  // 새 카드 확인
  if ( ! mfrc522.PICC_IsNewCardPresent()) return; 

  // 카드 읽기
  if ( ! mfrc522.PICC_ReadCardSerial()) return;

  //카드 확인 메세지 음
  tone(TONEPIN ,523,1000/8);     // 도음으로 8분음표(음길이)
  delay(1000/4*1.30);             
  noTone(TONEPIN); 

  //읽은 Card UID와 등록된 Card UID가 일치하는 확인
  if (mfrc522.uid.uidByte[0] == cardkeyByte[0] && mfrc522.uid.uidByte[1] == cardkeyByte[1] &&
        mfrc522.uid.uidByte[2] == cardkeyByte[2] && mfrc522.uid.uidByte[3] == cardkeyByte[3] ){
   
         state=!state;    
         if(state == true){
          Serial.println("Open");
          mySerial.println("Open");
          servo.write(180);
          delay(1000);
   }
   else{          
          Serial.println("Close");
          mySerial.println("Close");
          servo.write(0);
          delay(1000);
         }
         delay(2000);
   }  
}

2) 결과


아두이노 IDE 시리얼모니터로 "Open" or "Close" 메세지 출력


실제 블루투스로 스마트폰에 깔리 블루투스 앱에 "Open" or "Close" 메세지 출력


작동 동영상 결과는 다음과 같습니다.


2. RFID-RC522 + 초음파 센서



RFID-RC522 모듈로 출입문이 열리고 닫히는 동작을 한다고 상상을 해보세요. 이때 출입문에 RFID-RC522 Card를 대면 Servo Motor를 180도 회전 시켜서 "Open" 상태가 되고 출입문를 지나가면 자동으로 Servo Motor가 0도로 회전 시켜서 "Close" 상태를 만든 다면 좀 더 그럴싸 해지겠죠. 이 표현을 하기 위해서 여러 Sensor 중 초음파 Sensor를 사용하여 그 느낌을 표현 하고자 합니다. 즉, 출입문을 지나고 출입문이 닫혀도 되는 안전지점에 초음파 Sensor를 배치하고 인간 감지를 통해 출입문을 자동으로 닫히게 한다는 설정으로 상상을 해 보았습니다. 물론 이 경우는 상황 조건 변수들을 다 고려한다면 좀 복잡해지지만 최대한 단순하게 의미만 전달하는 실험을 하는게 목적임으로 단순하게 접근하고 단순한 동작만 수행되도록 실험 하겠습니다.
실제로 모형을 만들어서 실험을 하면 좋은데 손재주가 없어서 못 만들었네요. 대충 그 의미만 전달하는 실험으로 Card를 RFID-RC522모듈에 대면 Servo Motor가 180도 회전을 하고 초음파 Sensor가 측정한 거리값을 일정 수치 이하면 인간을 감지한 걸로 판정하고 Servo Motor를 0도로 회전시키는 실험으로 그 느낌을 대신 전달하고자 합니다.


1) RFID-RC522 + 초음파 센서 회로도



부품이 하나씩 늘어날 때마다 회로도가 지져분 해지네요. 처음에는 초음파센서 두개로 연결해서 상황을 하나 더 만들려고 했는데 엄청 지져분 해져서 한개의 초음파 센서로 단순하게 초음파센서로 읽은 거리값으로 동작하게 표현하여 최대한 코딩량을 줄였네요.


2) 코딩


  • 내용 : RFID-RC522 리더기로 읽은 Card가 인식되면 Servo Motor를 180도 회전 시키고 초음파 센서로 일정거리값이하면 Servo Motor를 0도로 회전 시킨다.(거리값=10cm 실험)

위 소스를 약간만 변경하겠습니다.

  //읽은 Card UID와 등록된 Card UID가 일치하는 확인
  if (mfrc522.uid.uidByte[0] == cardkeyByte[0] && mfrc522.uid.uidByte[1] == cardkeyByte[1] &&
        mfrc522.uid.uidByte[2] == cardkeyByte[2] && mfrc522.uid.uidByte[3] == cardkeyByte[3] ){
   
         state=!state;    
         if(state == true){
          Serial.println("Open");
          mySerial.println("Open");
          servo.write(180);
          delay(1000);
   }
   else{          
          Serial.println("Close");
          mySerial.println("Close");
          servo.write(0);
          delay(1000);
         }
         delay(2000);
   }  

위 소스에서 필요한 동작은 Card가 일치하면 문만 열기게 하면 되니깐 else 이하문은 필요 없습니다. 이 부분은 초음파센서 코딩으로 넘기게 됩니다.

<
  if (mfrc522.uid.uidByte[0] == cardkeyByte[0] && mfrc522.uid.uidByte[1] == cardkeyByte[1] &&
        mfrc522.uid.uidByte[2] == cardkeyByte[2] && mfrc522.uid.uidByte[3] == cardkeyByte[3] ){
   
         state=true;
                 
         Serial.println("Open");
         mySerial.println("Open");
         servo.write(180);
         delay(1000);
    }

이렇게 해서 Card가 읽치하면 무조건 Servo Motor는 180도로 향하게 됩니다. 여기서 state 상태변수를 그냥 남겨둿는데 이것은 초음파센서 진입 변수로 재활용 할 예정입니다. 즉, Card를 대고 문이 열렸을 때만 초음파센서를 작동시킨다고 보시면 됩니다.

초음파 센서핀은 Trig, Echo핀이 있는데 Trig은 초음파를 쏘는 핀이고 Echo은 초음파를 읽는 핀입니다.

const int TRIG_PIN = 4;
const int ECHO_PIN = 5;

남는 핀중에 4,5번핀을 그냥 선택했습니다.

void setup(){
  pinMode(TRIG_PIN,OUTPUT); //초음파센서핀(TRIG)
  pinMode(ECHO_PIN,INPUT); //초음파센서핀(ECHO)
}

그리고 Trig(OUTPUT), Echo(INPUT)핀을 어떤 모드로 사용할 것인지 선언해야 합니다.

if(state == true){   
     float distance = UltrasonicDistance();
     if(distance<10){
          state=false;
          Serial.print("Close : "); 
          Serial.println(distance);          
          mySerial.println("Close");
          servo.write(0);
          delay(500);
      }
}   

state로 Card가 일치하면 문이 열리잖아요 그때 state가 true가 되니깐 초음파센서는 그 때부터서 초음파 센서가 동작해야 하기 때문에 이렇게 if문으로 "state==true"일 때 UltrasonicDistance()함수로 초음파 센서를 측정하겠다는 로직입니다. UltrasonicDistance()함수는 사용자정의함수로 직접 만든함수명입니다.

float UltrasonicDistance(){  
  digitalWrite(TRIG_PIN, LOW); 
  delayMicroseconds(2); 
  digitalWrite(TRIG_PIN,HIGH); 
  delayMicroseconds(10); 
  digitalWrite(TRIG_PIN,LOW); 

  float duration = pulseIn(ECHO_PIN, HIGH);  
  return duration / 57.5;  
}

초음파 측정 로직인데 이 로직은 기본 아두이노홈페이지에 가시면 측정하는 코딩 로직을 예제로 소개하고 있습니다. 그 부분을 묶어서 캡슐화 해서 사용자정의 함수로 분리해 냈습니다. 이 코딩을 loop()안에다가 전부 코딩하면 오히려 가독성이 떨어지기 때문에 사용자정의함수로 분리를 한 것이죠.

     if(distance<10){
           거리가 10cm이하면 참;
             state = false;
             servo.write(0);
             delay(500);
     }

거리가 10cm이하면 문이 닫힌다는 설정문입니다. 그러면 문이 닫히면 더이상 초음파센서 측정이 필요 없으니깐 state=false로 바꾸어 주면 다시 문이 열리기 전까지는 초음파 측정을 할 필요가 없겠죠. state가 초음파센서 작동 진입 락이라고 생각하시면 됩니다.

초음파센서 로직의 대한 코딩 배치를 하면 종합 코딩은 다음과 같습니다. 새카드확인과 카드읽기 전에 초음파센서를 동작시킬건지 말건지를 먼저 수행되도록 배치 했습니다. 그 이유는 새카드확인과 카드읽기가 만족하지 않으면 거기서 return 되기 때문에 새카드확인과 카드읽기 문장 아래에 배치하면 카드를 대기 전까지 초음파센서는 측정하지 못하게 됩니다. 그래서 먼저 배치 한 것이죠. 그리고, state 상태변수로 초음파센서를 작동 상태값으로 초음파센서를 제어하게 됩니다.

[소스]

#include <SPI.h>
#include <MFRC522.h>
#include <Servo.h>
#include <SoftwareSerial.h>

const int RST_PIN = 9;
const int SS_PIN = 10;

MFRC522 mfrc522(SS_PIN, RST_PIN);   // MFRC522 인스턴스

byte cardkeyByte[4] = {0x55, 0xAF, 0x07, 0x88}; //card UID 
boolean state = false; //초음파 센서 작동 상태값

Servo servo;
const int SERVO_PIN = 7;
const int TONEPIN = 6;

const int rxPin = 2;
const int txPin = 3;
SoftwareSerial mySerial(rxPin, txPin); // RX, TX

const int TRIG_PIN = 4;
const int ECHO_PIN = 5;


void setup() {
  Serial.begin(9600);  
  while (!Serial);  
  mySerial.begin(9600);     
  SPI.begin();         //SPI 시작
  mfrc522.PCD_Init();  //MFRC522 card 초기화
  Serial.println(F("Warning: this example clears your mifare UID, use with care!"));

  pinMode(TRIG_PIN,OUTPUT); //초음파센서핀(TRIG)
  pinMode(ECHO_PIN,INPUT); //초음파센서핀(ECHO)

  servo.attach(SERVO_PIN);
  servo.write(0);
  delay(50);
}

void loop() {

  if(state == true){   
     float distance = UltrasonicDistance();
     if(distance<10){
          state=false;
          Serial.print("Close : "); 
          Serial.println(distance);          
          mySerial.println("Close");
          servo.write(0);
          delay(500);
      }
   }    
  // 새 카드 확인
  if ( ! mfrc522.PICC_IsNewCardPresent()) return; 

  // 카드 읽기
  if ( ! mfrc522.PICC_ReadCardSerial()) return;

  //카드 확인 메세지 음
  tone(TONEPIN ,523,1000/8);     // 도음으로 8분음표(음길이)
  delay(1000/4*1.30);             
  noTone(TONEPIN); 

  //읽은 Card UID와 등록된 Card UID가 일치하는 확인
  if (mfrc522.uid.uidByte[0] == cardkeyByte[0] && mfrc522.uid.uidByte[1] == cardkeyByte[1] &&
        mfrc522.uid.uidByte[2] == cardkeyByte[2] && mfrc522.uid.uidByte[3] == cardkeyByte[3] ){
   
          state = true;    
          Serial.println("Open");
          mySerial.println("Open");
          servo.write(180);
          delay(500);
   }  
}
float UltrasonicDistance(){  
  digitalWrite(TRIG_PIN, LOW); 
  delayMicroseconds(2); 
  digitalWrite(TRIG_PIN,HIGH); 
  delayMicroseconds(10); 
  digitalWrite(TRIG_PIN,LOW); 
    
  float duration = pulseIn(ECHO_PIN, HIGH);  
  return duration / 57.5;  
}

3) 결과


아두이노 IDE 시리얼모니터로 "Open" or "Close" 메세지 출력과 함께 초음파센서가 측정된 거리(Cm)을 동시에 출력 합니다.



스마트폰에는 이전 소스와 동일하게 "Open" or "Close" 메세지 출력 합니다.


동작 동영상은 다음과 같습니다.


마무리


RFID-RC522 모듈에 부품이 하나씩 늘어나면서 코딩량이 좀 많이 늘어 났습니다. 만약, 처음부터 이렇게 회도도를 만들고 코딩량이 길었다면 아마도 RFID-RC522 모듈에 대해 이해하기가 힘들었을 꺼에요. 그리고, 이전 post를 읽지 않는다면 오늘 post가 어려울 수도 있으니깐 꼭 읽고 이 post를 보셨으면 합니다.

최근의 post를 계속 중복된 실험이 이루어지고 있는데 그 중복되 실험 의미를 잘 이해하시고 따라오셔야 합니다. 대개 개별적인 부품 실험은 쉽게 이해하시는데 2개 이상의 부품이 결합하면 어떻게 코딩해야 할지 이해하지 못하는 경우가 많습니다. 어디에 어느 명령 코딩을 삽입해야 하는지를요. RFID-RC522 모듈을 이렇게 작동합니다. 하고 끝내면 사실 이 모듈로 상상을 하더라도 다른 부품과 연결 하기는 초보분들은 쉽지 않습니다. 여러개의 부품을 연결하면 코딩에 멘붕이 발생하기도 합니다. 제가 실험한 과정을 잘 보시고 어떤 과정으로 회로도를 만들고 코딩을 했는지 이해하셨으면 합니다.

RFID-RC522 부품을 하나에서 출발해서 여기까지 왔습니다. 부족한 부분이 많고 추가해야 할 부품은 많지만 상상의 상상을 더하면 위 회로도가 아닌 여러분만의 회로도를 만들 수 있으니깐 RFID-RC522 모듈 하나에서 다시 처음부터 여러분들이 회로도를 만들고 상황을 설정하고 그 상황의 회로도를 만들고 그 상황의 코딩을 한번 해보셨으면 합니다.


댓글()

[아두이노] RFID-RC522 + Servo Motor 제어

IOT/아두이노|2019. 5. 7. 09:00

[아두이노] RFID-RC522 + Servo Motor 제어



지난 시간에 RFID-RC522를 아두이노에 연결하여 제대로 인식하는지에 대해 예제를 통해 간단히 테스트를 하였습니다. MFRC522 라이브러리에서 초기 선언하는 세팅 부분만 간단히 살펴 보았기 때문에 뭔가 제어하는 실험이 부족한 것 같아서 오늘은 Servo Motor를 같이 연결하여 RFID-RC522를 제어하는 실험을 하고자 합니다. 어제 배웠던 내용에서 크게 변화 된 것은 없습니다. 단지 카드 UID 값을 읽고 그 값에 일치 하면 Servo Motor가 특정 각도로 회전이 일어나도록 하는 실험을 하고자 합니다. 일상에서의 도어락이나 전철을 탈때 출입 통로, 회사 출입 통로 등의 카드를 대면 인식하고 출입을 할 수 있도록 문을 열어주는 동작과 느낌을 실험하고자 Servo Motor의 Open 회전값과 Close 회전값을 주어 비슷한 느낌을 표현 하였습니다.


1. RFID-RC522 + Servo Motor 회로도



  • 준비물 : RFID-RC522, Servo Motor, 피에조부저, 아두이노우노
  • 내용 : SPI 통신을 할 수 있게 핀은 연결하고 Servo Pin 7번, 피에조부저 6번으로 연결해 보자.
  • 지난시간 회로도 참고 : RFID-RC522 제어(아두이노)


출처 : Fritzing

RFID-RC522 회로도가 좀 복잡해 보일 꺼에요. 지난 시간에 RFID-RC522 회로도에서 Servo Motor와 피에조부저 핀을 추가 하시면 됩니다. 오늘 회로도를 보고 선을 연결하지 마시고 링크 걸린 지난 시간의 회로도를 보고 회로도 선을 연결한 뒤에 오늘사용하는 7번 Servo 핀과 6번 피에조 핀만 연결하면 되니깐 그렇게 어렵지 않을 꺼에요.

2. 코딩



MFRC522

#include <SPI.h>
#include <MFRC522.h>
  • MFRC522 mfrc522(SS_PIN, RST_PIN) : MFRC522 인스턴스화
  • SPI.begin() :SPI bus 초기화
  • mfrc522.PCD_Init() : MFRC522 card 초기화
  • mfrc522.PICC_IsNewCardPresent() : 새카드 확인
  • frc522.PICC_ReadCardSerial() : 하나의 카드읽기 확인
  • rfid.uid.uidByte[] : 읽은 카드 키값이 들어 있음

피에조 부저

  • tone(tonePin ,음,음길이) : 음 시작
  • noTone(tonePin ) : 음 중지

Servo Motor

#include <Servo.h>
  • Servo servo : 서보모터 객체 선언
  • servo.attach(서보핀) : 서보 모터에 출력을 담당할 핀 선언
  • servo.write(회전각) : 회전각 만큰 서보모터를 회전 시킴

시리얼 통신

  • Serial.begin(9600) : 시리얼 통신 시작(9600 통신속도)
  • Serial.println(값) : 시리얼모니터 출력
  • Serial.available() : 시리얼통신으로 데이터가 들어놨는지 상태 확인
  • Serial.parseInt() : Int형으로 데이터를 읽음

지난 시간에 이여서 오늘도 위 함수 부분은 그대로 Post에 남겨 뒀습니다. 지난 시간의 Post를 왔다 갔다 하실 필요 없이 오늘 Post만 보시고 코딩을 생각할 수 있게 그대로 가져 왔네요.

[소스] MFRC522의 예제 중 하나( 출처 : miguelbalboa의 라이브러리)

#include <SPI.h>
#include <MFRC522.h>

#define RST_PIN         9       
#define SS_PIN          10      

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

void setup() {
    Serial.begin(9600);     // Initialize serial communications with the PC
    while (!Serial);        // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4)
    SPI.begin();            // Init SPI bus
    mfrc522.PCD_Init();     // Init MFRC522
    mfrc522.PCD_DumpVersionToSerial();  // Show details of PCD - MFRC522 Card Reader details
    Serial.println(F("Scan PICC to see UID, SAK, type, and data blocks..."));
}

void loop() {
    // Look for new cards
    if ( ! mfrc522.PICC_IsNewCardPresent()) {
        return;
    }

    // Select one of the cards
    if ( ! mfrc522.PICC_ReadCardSerial()) {
        return;
    }

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

지난 시간에 라이브러리에 있던 예제 소스입니다. 이 소스를 기반으로 Servo Motor를 제어 하겠습니다. 우선 위 소스를 DumpInfo 예제를 한번 실행 시켜 주세요.

Card UID: 55 AF 07 88

Card UID의 값을 확인 하세요. 참고로 제가 실험하는 Card UID 값하고 여러분이 실험하는 Card UID는 다릅니다. 이 Card UID로 실험하기 때문에 한번 실행해서 자신의 Card UID를 알아내야 합니다. 확인이 되면 이 Card UID 값을 배열 변수로 저장 하세요.

byte CardUidByte[4] = {0x55, 0xAF, 0x07, 0x88};

참고로, 숫자가 알파벳이 나오면 16진수라고 여기세요. 여기서 그냥 숫자를 기입하시면 안되고 앞에 '0x'을 꼭 붙여주셔야 이 수가 16진수이구나 하고 프로그램이 인식합니다. 이렇게 해서 비교 할 Card UID값을 저장해 놓았습니다.

카드를 읽게 되면 그 값은 어디에 들어 있을까요.

mfrc522.uid.uidByte[]

이곳에 담겨져 있습니다. 4개의 UID 숫자값이 Card UID 값을 가지고 있으니 mfrc522.uid.uidByte[0] ~ mfrc522.uid.uidByte[3] 까지해서 그 값을 가지고 있습니다. 그러면, IF문으로 해서 저장된 값과 비교하면 됩니다.

  if (mfrc522.uid.uidByte[0] == CardUidByte[0] && mfrc522.uid.uidByte[1] == CardUidByte[1] &&
        mfrc522.uid.uidByte[2] == CardUidByte[2] && mfrc522.uid.uidByte[3] == CardUidByte[3] ){
                읽어온 Card UID 값과 저장된 Card UID값이 일치하면 참;
                명령문;
  }

이렇게 해서 카드를 RFID-RC522에 대면 두 Card UID 값을 서로 비교하게 됩니다. 그리고 일치하면 if문 안의 명령문을 수행하게 됩니다.

코딩은 거의 다 했습니다. 이제는 Servo Motor를 회전 시켜야 겠죠. 카드의 UID가 일치하니깐 Servo Motor가 Open 시키는 의미로 일정 각도로 회전 시키는 동작과 다시 카드를 RFID-RC522에 대면 Servo Motor가 Close 시키는 의미로 원래 각도로 회전 시켜는 동작을 표현 할 에정입니다. Card를 대면 Open or Close가 교차로 반복되는 실험입니다.

이 동작을 수행하기 위해서는 어떻게 코딩해야 할까요.

boolean state = false;

이렇게 하나의 ServoMotor의 상태값 변수를 만들어 놓습니다. 그리고 나서 아래와 같은 코딩을 하면 됩니다.

  if (mfrc522.uid.uidByte[0] == CardUidByte[0] && mfrc522.uid.uidByte[1] == CardUidByte[1] &&
        mfrc522.uid.uidByte[2] == CardUidByte[2] && mfrc522.uid.uidByte[3] == CardUidByte[3] ){
          
          state=!state;  //Servo Motor(Open or Close)
                
          if(state == true){
           Serial.println("Open");
           servo.write(180);
           delay(1000);
          }
          else{           
           Serial.println("Close");
           servo.write(0);
           delay(1000);
          }
          delay(2000);              
  }

초기 state은 false 상태이고 Cade UID 값이 일치하면 state은 반전 true가 되어 다음 if문에서 "state == ture"이면 참이니깐 "Open"으로 Servo Motor를 180도 회전 시키고 1초 대기 했다가 다시 연속으로 카드값이 인식하지 못하도록 2초동안 딜레이 시간을 추가로 더 연장 했습니다. 총 3초 동안은 Card가 인식되지 못하게 했습니다. 3초가 지난 후 Card가 다시 RFID-RC522에서 인식하면 state은 !state로 반전으로 state은 false가 되어 다음 if문 "state == ture"가 거짓으로 else 이하 문장을 수행 합니다. 그러면 "Close"로 Servo Motor은 0도로 원래 각도로 회전 되어 돌아오게 됩니다. Card가 RFID-RC522에 대면 교차로 "Open" 과 "Close"가 반복 됩니다.

이렇게 해서 Card로 Servo Motor를 회전 시킬 수 있게 되었습니다.

여기서, 추가로 Card가 읽혔는지 확인 할 방법이 없습니다. Servo Motor가 회전 되었을 때 확인이 됩니다. 즉, 카드를 Servo Motor가 회전 될 때 까지 RFID-RC522에 대고 있어야 한다는 소리가 됩니다. 좀 불편하죠.

그래서, 카드를 읽은 순간 그때 뭔가 메세지를 외부로 보여준다면 이 문제가 해결 할 수 있습니다. 그 역할을 소리로 표현하기 위해서 피에조부저를 이용하고자 합니다.

  // 새 카드 확인
  if ( ! mfrc522.PICC_IsNewCardPresent()) return; 

  // 카드 읽기
  if ( ! mfrc522.PICC_ReadCardSerial()) return;
    
  tone(tonePin ,523,1000/8);     // 도음으로 8분음표(음길이)
  delay(1000/4*1.30);             
  noTone(tonePin ); 

이렇게 카드가 읽기와 카드를 비교하기 전 사이에 사이에 피에조부저의 음이 울리게 하면 쉽게 확인이 되겠죠.

종합해 보면,

[소스]

#include <SPI.h>
#include <MFRC522.h>
#include <Servo.h>

const int RST_PIN = 9;
const int SS_PIN = 10;

MFRC522 mfrc522(SS_PIN, RST_PIN);   // MFRC522 인스턴스

byte CardUidByte[4] = {0x55, 0xAF, 0x07, 0x88}; //card UID 
boolean state = false; //Servo Motor 상태값

Servo servo;
const int SERVO_PIN = 7;
const int TONEPIN = 6;

void setup() {
  Serial.begin(9600);  
  while (!Serial);     
  SPI.begin();         //SPI 시작
  mfrc522.PCD_Init();  //MFRC522 card 초기화
  Serial.println(F("Warning: this example clears your mifare UID, use with care!"));

  servo.attach(SERVO_PIN);
  servo.write(0);
  delay(50);
}

void loop() {
  // 새 카드 확인
  if ( ! mfrc522.PICC_IsNewCardPresent()) return; 

  // 카드 읽기
  if ( ! mfrc522.PICC_ReadCardSerial()) return;

  //카드 확인 메세지 음
  tone(tonePin ,523,1000/8);     // 도음으로 8분음표(음길이)
  delay(1000/4*1.30);             
  noTone(tonePin ); 

  //읽은 Card UID와 등록된 Card UID가 일치하는 확인
  if (mfrc522.uid.uidByte[0] == CardUidByte[0] && mfrc522.uid.uidByte[1] == CardUidByte[1] &&
        mfrc522.uid.uidByte[2] == CardUidByte[2] && mfrc522.uid.uidByte[3] == CardUidByte[3] ){
   
         state=!state;    
                 
         if(state == true){
           Serial.println("Open");
           servo.write(180);
           delay(1000);
          }
          else{
           Serial.println("Close");
           servo.write(0);
           delay(1000);
         }
         delay(2000);
   }  
}

3. 결과



4. 추가 코딩


위 코딩은 약간 도어락 같은 느낌 이였다면 출입문일 경우는 카드로 열고 닫고를 안하고 한번 대면은 열리고 나서 일정시간이 지나면 닫히게 됩니다. 출입문 경우는 어떻게 코딩할까요.

위 소스에서 한 부분만 수정하면 됩니다.

  //읽은 Card UID와 등록된 Card UID가 일치하는 확인
  if (mfrc522.uid.uidByte[0] == CardUidByte[0] && mfrc522.uid.uidByte[1] == CardUidByte[1] &&
        mfrc522.uid.uidByte[2] == CardUidByte[2] && mfrc522.uid.uidByte[3] == CardUidByte[3] ){
   
           Serial.println("Open");
           servo.write(180);
           delay(5000);
           Serial.println("Close");
           servo.write(0);
           delay(1000);                 
   }  

이렇게 수정하시면 끝납니다. 실험에서는 5초 동안 열렸다가 다시 닫히는 걸로 했는데요. 실제 출입문이면 이러지는 않겠죠. 적어도 수십초는 열려 있다가 닫혀야 겠죠. 아니면 초음파 센서같은 인간 감지 센서를 이용하여 지나 가면 자동으로 닫히게 하면 되겠죠. 그렇게 하자면 부품이 추가로 늘어나고 오히려 오늘 전달하고자 하는 의미가 제대로 전달되지 않기 때문에 이정도 까지만 하겠습니다.

[결과]


마무리


RFID-RC522 라이브러리는 쉽지 않지만 간단한 부분만 가져다가 응용하면 그래도 재밌는 표현들을 할 수 있습니다. 여러분들도 한번 이 RFID-RC522 모듈을 구하셔서 실험을 해보셨으면 합니다. 가격도 비싸지 않고 싼편이라서 괜찮은 실험 도구라고 생각 되네요.

아두이노우노에서 실험하면 SPI 통신을 하기 때문에 사실 다른 부품을 추가 할 자리가 많지 않습니다. 그럴 때는 두개 이상의 아두이노로 통신을 통해 제어하면 좋은데 그 실험을 하려면 2대정도 있어야 하는데 한대 뿐이라 아쉽네요.

아무튼 오늘 배운 내용을 토대로 다른 부품 특징들을 머리속에서 떠올려서 RFID-RC522와 연결하는 상상을 한번 해보셨으면 합니다.


댓글()

[아두이노] RFID-RC522 제어

IOT/아두이노|2019. 5. 6. 09:00

아두이노] RFID-RC522 제어



오늘은 RFID-RC522 모듈을 살펴보고자 합니다. 이 모듈은 일상에서 교통카드, 출입문 카드, 도어락 카드와 같은 것들을 아두이노상에서 표현할 수 있는 모듈입니다. 카드에 등록된 정보를 RFID-RC522 리더기가 읽고 그 읽은 값을 통해서 우리는 여러가지를 표현할 수 있습니다. RFID 모듈은 통신 방식이 SPI여서 좀 까다롭습니다. I2C 모듈이면 좀 더 사용하기가 편할 텐데 말이죠. 실험에서는 카드 인식에 문제가 생겨서 아래 사진에서 보는 것처럼 열쇠 고리 모양 같은걸로 간단히 실험을 하였네요. 이제부터서 RFID-RC522 모듈을 아두이노 핀에 어떻게 연결하고 코딩할 때 필요한 MFRC522 라이브러리를 설치에 대해서 알아본 뒤에 라이브러리드 안에 있는 DumpInfo 예제를 통해서 카드 정보를 읽는 실험을 하겠습니다.


1. RFID-RC522





출처 : Fritzing

RFID-RC522 모습은 위 출처가 링크 된 곳에서 다운로드 하신 후 Fritzing에 등록하셔서 디자인 하시면 됩니다. 핀 번호는 위에서 부터 아래로 순서대로 아래 표를 참조하시면 되겠습니다.


SPI 통신을 하기 때문에 아두이노의 핀과 RFID-RC522 핀을 위 표처럼 연결하시면 됩니다. 어떤 RFID 모듈은 I2C통신을 하는 모듈이 있는데 그럴 경우는 아날로그 핀 A4, A5으로 연결하셔서 실험하시면 됩니다. 자신이 사용하는 모듈은 어떤 방식인지를 우선 구별하시고 핀을 연결하시면 됩니다.

2. RFID-RC522 회로도


  • 준비물 : RFID-RC522, 아두이노우노
  • 내용 : SPI 통신을 할 수 있게 핀은 연결한다.


선 연결이 좀 복잡해 보이지만 위 표를 보시고 선을 연결하시면 어렵지 않을 거라 생각됩니다. 그래도 이해가 안되시면 아래 스케메틱 회로도를 보고 선을 연결하시면 되겠습니다.



출처 : Fritzing

3. RFID-RC522 라이브러리 추가


rfid로 검색하시면 되는데 직접 MFRC522로 검색어로 검색하셔도 됩니다. 아래처럼 검색이 되면 이 라이브러리를 설치하시면 됩니다.


4. 코딩



MFRC522

#include <SPI.h>
#include <MFRC522.h>
  • MFRC522 mfrc522(SS_PIN, RST_PIN) : MFRC522 인스턴스화
  • SPI.begin() :SPI bus 초기화
  • mfrc522.PCD_Init() : MFRC522 초기화
  • mfrc522.PICC_IsNewCardPresent() : 새카드 확인
  • frc522.PICC_ReadCardSerial() : 하나의 카드읽기 확인
  • rfid.uid.uidByte[] : 읽은 카드 키값이 들어 있음

miguelbalboa의 라이브러리 안에 위 함수들이 있는데 여러가지 함수들이 있는데 가장 기본적인 것만 설명하기 때문에 꼭 기억해 주세요. 저도 이 라이브러리를 사용하여 간단히 테스트를 했지만 따로 만들고 싶은 것이 없어서 그냥 간단히 테스트만 했네요. 제대로 RFIC-RC522를 사용하기 위해서는 링크된 라이브러리에 가셔서 코딩을 제대로 이해하시고 사용하실 수 있을 꺼에요.

MFRC522 라이브러리를 설치하면 여러개의 예제가 있는 데 한번씩 다 사용해서 어떤 결과가 나오는지 확인 해보시기 바랍니다.

여러 예제들 중에서 소스 코딩이 짧아보이는 DumpInfo라는 예제가 있습니다. 카드 정보를 읽어와서 출력하는 예제인데 이걸로 정상 작동하는지 살펴 볼께요.

[소스] MFRC522의 예제 중 DumpInfo( 출처 : miguelbalboa의 라이브러리)

#include <SPI.h>
#include <MFRC522.h>

#define RST_PIN         9          // Configurable, see typical pin layout above
#define SS_PIN          10         // Configurable, see typical pin layout above

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

void setup() {
    Serial.begin(9600);     // Initialize serial communications with the PC
    while (!Serial);        // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4)
    SPI.begin();            // Init SPI bus
    mfrc522.PCD_Init();     // Init MFRC522
    mfrc522.PCD_DumpVersionToSerial();  // Show details of PCD - MFRC522 Card Reader details
    Serial.println(F("Scan PICC to see UID, SAK, type, and data blocks..."));
}

void loop() {
    // Look for new cards
    if ( ! mfrc522.PICC_IsNewCardPresent()) {
        return;
    }

    // Select one of the cards
    if ( ! mfrc522.PICC_ReadCardSerial()) {
        return;
    }

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

[실행]


리더기가 정상적으로 인식했네요.

소스 코딩을 보면 MFRC522의 객체변수를 mfrc522를 선언할 때 RST, SS Pin을 두개를 인자로 인스턴스화 하네요.

SPI.begin();            // Init SPI bus
mfrc522.PCD_Init();     // Init MFRC522

이렇게 해서 초기화 작업을 끝냈고 장상적으로 인식하는지 테스트가 진행 됩니다.

mfrc522.PCD_DumpVersionToSerial();

위 실행 결과에서 정상적으로 "Firmware Version: 0x88= (clone)" 라고 떴지만 인식을 안하면 실패한 에러 메세지가 출력 됩니다.

정상적으로 인식 했으니깐 카드를 읽을 준비를 합니다.

두개의 if문이 loop()함수에서 나옵니다.

mfrc522.PICC_IsNewCardPresent() 새카드 확인
mfrc522.PICC_ReadCardSerial() 카드 읽기

새로운 카드를 확인하면 다음 카드 읽기가 진행됩니다. 이 두 과정을 IF문으로 이렇게 표현하면 어떻게 동작 할까요.

if(!조건식) return ;

이 명령라인의 의미는 조건식이 거짓일 때 참이 됩니다. 족건식이 거짓이면 if문이 참이되어 return 명령을 만나는데 이 명령은 현재 명령 범위에서 아래 명령을 수행 할 필요 없이 다시 그 명령 범위를 빠져 나오라는 의미가 됩니다.

void loop(){
  if(!조건식) return ;
    명령문1;
    명령문2;
}

이렇게 되어 있으면 조건식이 거짓이면 return 명령으로 명령문1, 명령문 2를 수행하지 않고 빠져나와 종료된다고 생각하시면 돼요. loop()은 문한 반복이니깐 빠져나왔지만 loop()함수가 처음부터 다시 수행됨으로 if문의 조건식이 참인지 거짓인지 계속 무한 판정을 하게 됩니다. 이때 if문이 거짓이 되면 return 명령을 수행하지 않고 다음 명령문1, 명령문2가 수행되게 됩니다. 어떤 의미인지 아시겠죠.

위 소스에서 PICC_IsNewCardPresent()함수로 새카드인지 확인하고 카드가 확인되면 if문에서 새카드 확인되지 못할 때만 return 명령을 수행하기 때문에 확인되면 다음으로 넘어 갑니다. PICC_ReadCardSerial() 확인된 새카드를 읽게 됩니다. 읽게 되면은 if문에서 읽지 못할때 return 명령을 수행하기 때문에 읽었으니깐 다음 명령으로 넘어 가게 됩니다.

이렇게 두단계로 새카드 확인과 카드읽기로 락을 걸어놓은 것이죠.

mfrc522.PICC_DumpToSerial(&(mfrc522.uid));

Dump 정보를 시리얼모니터로 출력시키는 명령입니다.

5. 결과


아래와 같이 카드를 리더기에 올려놓으면 카드의 정보를 읽어오게 됩니다.


5. RFID-RC522 인식이 에러 해결책


아래와 같은 메세지가 인식하지 못할 때 뜨게 됩니다.


첫번째, 인식 실패의 원인은 접촉 불량입니다. 처음에 잘 작동되더라도 나중에 쓰다보면 잘 인식하지 못합니다. 그래서 이 모듈을 사용하는 분들은 대부분 납땜을 많이 합니다.

두번째, 기존 아두이노에 이식 된 프로그램에 새로 업로드 한 프로그램과 그 사이에 리더기의 동작 에러가 발생 할 수 있습니다. 전원을 끄고 다시 접속해서 한번 문제가 생기면 사실 재 인식이 되지 않는 경우가 발생 합니다. 그럴 때 접촉 불량인가 하고 다시 연결선들을 점검하는데 혹시 이런 문제로 인해 인식을 못할 수 있으니깐요. 다른 프로그램을 아두이노에 업로해서 돌려 본 뒤에 다시 RFID-RC522 소스를 돌려보세요. 저도 방금 전 잘 인식되던게 인식 에러 상황을 만들려고 Gnd 선을 빼고 프로그램을 업로드 한뒤에 정상적으로 선 연결하고 업로드 했더니 RFID-RC522가 인식을 안하더군요. 선 접촉 불량인가 하고 삽질을 하다가 그냥 기본 예제인 Blink를 아두이노에 업로드 하고 나서 다시 RFID-RC522 소스를 업로드 하니깐 그때서야 정상적으로 인식이 되었습니다.

마무리


RFID-RC522 라이브러리가 참 쉽지 않는 라이브러리 입니다. 사용하는 함수들이 많은데 사실 라이브러리 파일을 찾아가서 안에 함수 내용이 정확히 어떻게 코딩되어 있는지 확인해야 그 의미를 이해할 수 있습니다. 저도 이 RFID 리더기를 사용할 때 좀 버겁습니다. 단지 카드를 대면 리더기가 읽으면 카드 UID값이 rfid.uid.uidByte[] 변수에 저장되는데 카드 UID 값을 기준으로 간단한 제어만 할 수 있습니다.

이 부품은 응용 범위가 많습니다. 일상에서 하루에 한번씩은 접해 보셨을 거라 생각됩니다. 교통카드 충전할 때 그 상황을 생각해보세요. 또는 매장에 물건을 살 때 택을 찍을 때를 상황을 떠올려 보세요. 도서관이나 회사 입구를 지나갈때 카드를 대고 지나가는 상황을 떠올려 보세요. 교통카드로 전철이나 버스를 타기 위해서 교통카드를 찍을 때를 떠올려 보세요. 집 출입문에 도어락을 떠올려보세요. 이외에도 많은 곳에서 이와 비슷한 것들이 많습니다.

RFID-RC522를 이용하시는 분들은 한번 이 모듈을 이용하여 어떤 것을 표현하고 싶은지 상상을 해보시고 재밌어 보이면 해당 라이브러리를 한번 열어보시고 그 함수의 로직이 어떻게 코딩되어 있는지 제대로 공부해보세요. 응용 분야가 많아서 아이디어만 있으면 꽤 괜찮은 작품들을 만들어 낼 수 있을 거라 생각됩니다.

댓글()