본문으로 이동

아두이노:RFID(13.56MHz)

학교의 모든 지식. SMwiki
Sam (토론 | 기여)님의 2026년 9월 8일 (화) 07:17 판 (연결 예시)

아두이노 관련 정보를 모으기 위한 틀. 틀:아두이노

  1. 아두이노:개요
  2. 아두이노:하드웨어
    1. 아두이노:우노보드
    2. 아두이노:ESP32
  3. 아두이노:코드
    1. 아두이노:컴퓨터에서 보드로
      1. 아두이노:VSCode에서 작업
    2. 아두이노:안드로이드에서 보드로
    3. 아두이노:기초 문법
    4. 아두이노:라이브러리
  4. 아두이노:핀 사용
    1. 아두이노:전원공급
  5. 아두이노:시리얼 통신
    1. 아두이노:엑셀기록
    2. 아두이노:파이썬과 통신
  6. 아두이노:편의함수
  7. 센서 사용
    1. 아두이노:모션센서(미완)
    2. 아두이노:온습도센서
    3. 아두이노:조도센서(미완)
    4. 아두이노:인체감지센서(미완)
    5. 아두이노:pH미터 만들기(Gravity 아날로그 pH센서)
    6. 가스센서
      1. 아두이노:MOS 센서
  8. 아두이노:LCD 사용
  9. 아두이노:스위치 사용
  10. 아두이노:릴레이
  11. 통신 관련, 정보 교환
    1. 아두이노:2.4GHz 송수신
      1. 아두이노:WIFI
      2. 아두이노:블루투스(외장모듈)
      3. 아두이노:RF통신 nRF24L01+
    2. 아두이노:IR 송수신
    3. 아두이노:RFID
      1. 아두이노:RFID(13.56MHz)
  12. 아두이노:해결되지 않은 다양한 의문들
  13. 수업용 간단 실습
    1. 아두이노:LED
    2. 아두이노:초음파센서
    3. 아두이노:전압계 만들기
    4. 아두이노:저항계 만들기
  14. 아두이노 모터 출력
    1. 아두이노:서보 모터
    2. 아두이노:DC 모터
    3. 아두이노:BLDC 모터
  15. 키 조작
    1. 아두이노:아케이드 버튼 입력
    2. 아두이노:키보드 키 조작
  16. 미완문서
    1. 아두이노:스테핑 모터(미완)
  17. 분류:아두이노 프로젝트

개요

아두이노로 RFID 13.56MHz 규격을 다루는 것에 관한 문서.

RFID 자체에 관한 내용은 RFID 문서 참고

13.56 MHz 대역을 사용하는 RFID는 일반적으로 HF(High Frequency) RFID로 분류되며, 교통카드, 학생증, 사원증, 출입카드, 전자여권, NFC 태그 등에 활용된다.

아두이노에서는 RFID 리더 모듈을 연결하여 태그의 고유 식별번호인 UID를 읽거나, 지원되는 태그의 메모리에 데이터를 읽고 쓸 수 있다.

RFID 시스템의 구성

RFID 시스템은 일반적으로 다음과 같이 구성된다.

요소 설명 비고
태그(Tag)
  • 카드, 열쇠고리, 스티커 등의 형태로 제작됨.
  • UID와 사용자 데이터를 저장할 수 있음.
  • 수동형 태그는 별도의 배터리 없이 리더가 만드는 전자기장으로부터 동작에 필요한 에너지를 얻음.
리더(Reader)
  • 태그와 무선으로 통신함.
  • 태그의 UID 또는 저장된 데이터를 읽음.
  • 일부 리더는 태그 메모리에 데이터를 기록할 수 있음.
제어 장치
  • 아두이노, ESP32, 라즈베리파이 등의 마이크로컨트롤러 또는 컴퓨터를 사용함.
  • RFID 리더로부터 받은 정보를 처리함.
  • LED, 부저, 디스플레이, 도어록 등의 장치를 제어할 수 있음.

작동 원리

13.56 MHz RFID의 기본 동작 과정은 다음과 같다.

  • RFID 리더가 13.56 MHz의 전자기장을 형성함.
  • 리더 가까이에 있는 수동형 태그가 전자기장으로부터 동작에 필요한 에너지를 얻음.
  • 리더가 태그에 명령을 전송함.
  • 태그가 UID 또는 저장된 데이터를 응답함.
  • 리더가 응답을 수신하여 아두이노 등의 제어 장치로 전달함.
  • 제어 장치가 UID 확인, 출입 허용, 출결 기록 등의 작업을 수행함.

13.56MHz와 통신 규격의 구분

13.56 MHz는 RFID 통신에서 사용하는 반송파 주파수를 의미한다. 같은 13.56 MHz를 사용하더라도 모든 리더와 태그가 서로 호환되는 것은 아니다.

모듈 선택 기준

목적 적합한 모듈 예시 선택 이유
저비용 입문 실습 RC522 배선과 UID 읽기 실습이 비교적 간단함.
스마트폰 NFC와의 연동 PN532 또는 NFC 전용 모듈 RC522보다 NFC 관련 지원 범위가 넓음.
ISO/IEC 15693 태그 실습 해당 규격을 지원하는 리더 RC522는 일반적으로 ISO/IEC 15693을 지원하지 않음.
다양한 종류의 13.56 MHz 태그 실험 복수 규격을 지원하는 리더 13.56 MHz라는 주파수만으로는 태그 호환성을 보장할 수 없음.
출입통제 보안 실험 보안 기능을 지원하는 태그와 리더 단순 UID 비교보다 인증 및 암호화 기능이 필요함.

주의 사항

  • RFID 리더와 태그가 모두 13.56 MHz를 사용하더라도 통신 규격이 다르면 인식되지 않을 수 있음.
  • RC522 모듈은 일반적으로 3.3 V 전원을 사용함.
  • 안테나 주변에 금속이 있으면 인식 성능이 저하될 수 있음.
  • 태그와 리더의 방향 및 거리에 따라 인식 여부가 달라질 수 있음.
  • 하나의 리더가 모든 MIFARE 및 NFC 기능을 지원하는 것은 아님.
  • UID는 카드 종류에 따라 길이가 다를 수 있으므로 UID 길이를 고정하여 처리하지 않는 것이 좋음.
  • 결제카드나 실제 출입카드의 데이터를 임의로 읽거나 복제해서는 안 됨.
  • 실습에는 교육용으로 제공된 RFID 카드와 태그를 사용해야 함.

RFID와 NFC의 관계

NFC는 13.56 MHz 대역을 사용하는 근거리 무선 통신 기술이다. RFID와 관련된 기술이지만, 모든 13.56 MHz RFID 장치가 NFC의 모든 기능을 지원하는 것은 아니다.

따라서 스마트폰과의 통신이 필요한 경우에는 단순히 13.56 MHz를 지원하는지만 확인하지 말고, 사용하는 모듈이 필요한 NFC 동작 모드와 태그 규격을 지원하는지 확인해야 한다.

13.56 MHz의 주요 통신 규격

같은 주파수를 사용하는 태그와 리더라도 지원하는 통신 규격이 다르면 서로 통신할 수 없다.

규격 분류 사용 주파수 주요 특징 대표 기술 및 제품 주요 용도
ISO/IEC 14443 Type A 근접형 13.56 MHz Type A 방식의 변조, 부호화, 충돌 방지 및 카드 선택 절차를 사용함. MIFARE Classic, MIFARE Ultralight, MIFARE DESFire, NTAG 계열 교통카드, 출입카드, 학생증, NFC 태그
ISO/IEC 14443 Type B 근접형 13.56 MHz Type A와 같은 주파수를 사용하지만 변조, 부호화 및 초기화 방식이 다름. Type B 비접촉식 스마트카드 전자신분증, 전자여권, 비접촉식 스마트카드
FeliCa 근접형 13.56 MHz ISO/IEC 14443 Type A/B와는 다른 통신 방식을 사용하는 비접촉식 카드 기술임. FeliCa 카드 및 모바일 FeliCa 교통카드, 전자화폐, 출입관리
ISO/IEC 15693 근방형 13.56 MHz ISO/IEC 14443보다 비교적 먼 거리에서 태그를 식별하는 용도에 적합함. ICODE, ST25TV 계열 도서 관리, 자산 관리, 문서 추적, 산업용 태그
ISO/IEC 18000-3 품목 관리용 HF RFID 13.56 MHz 물품 식별과 관리를 위한 HF RFID 무선 인터페이스를 규정함. 산업용 HF RFID 태그 물류, 재고 및 품목 관리
NFC Forum 규격 NFC

(NFC 기기간 통신 가능)

13.56 MHz NFC 장치 간 상호 운용을 위한 태그 유형, 동작 방식 및 데이터 형식을 규정함. NFC Forum Type 1~5 Tag, NDEF 스마트폰 태그 인식, 정보 전달, 기기 연결

ISO/IEC 14443

ISO/IEC 14443은 13.56 MHz를 사용하는 근접형 비접촉식 카드 규격이다. 가까운 거리에서 카드를 인식하고 데이터를 빠르게 교환하는 용도에 적합하다. ISO/IEC 14443은 크게 Type A와 Type B로 구분된다.

구분 Type A Type B
사용 주파수 13.56 MHz 13.56 MHz
분류 근접형 카드 근접형 카드
통신 방식 Type A 방식의 변조, 부호화 및 초기화 절차 사용 Type B 방식의 변조, 부호화 및 초기화 절차 사용
대표 사례 MIFARE 계열, 일부 NFC 태그 전자신분증 및 비접촉식 스마트카드의 일부
상호 호환성 Type A만 지원하는 리더에서는 Type B 태그를 인식할 수 없음. Type B만

RC522 모듈

입문용으로 많이 사용하는 모듈.

항목 사양
동작 주파수 13.56 MHz
지원 규격 ISO/IEC 14443 Type A
지원 태그 MIFARE 및 NTAG 계열
일반적인 전원 3.3 V
대표 인식거리 최대 약 50 mm
규격 RC522 지원
ISO/IEC 14443 Type A 지원
ISO/IEC 14443 Type B 미지원
FeliCa 미지원
ISO/IEC 15693 미지원
ISO/IEC 18000-3 미지원
NFC Forum 규격 일부 Type A 태그만 제한적으로 인식

연결 예시

arduino_rfid_test_4.png

코드 작성 예시

일반 읽기

읽고 쓰기

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


// ========================================
// 핀 설정
// ========================================

constexpr uint8_t RST_PIN    = 9;
constexpr uint8_t SS_PIN     = 10;
constexpr uint8_t BUTTON_PIN = 2;


// RC522 객체
MFRC522 mfrc522(SS_PIN, RST_PIN);


// ========================================
// MIFARE Classic 설정
// ========================================

// MIFARE Classic 1K
constexpr uint8_t TOTAL_BLOCKS = 64;

// 한 블록은 16바이트
constexpr uint8_t BLOCK_SIZE = 16;

// 기본 Key A
MFRC522::MIFARE_Key key;


// 읽어 둔 카드 데이터
byte cardData[TOTAL_BLOCKS][BLOCK_SIZE];

// 데이터를 읽었는지 여부
bool dataReady = false;


// ========================================
// 초기화
// ========================================

void setup() {
    Serial.begin(9600);

    while (!Serial);

    // 버튼 설정
    // 버튼을 누르면 LOW
    pinMode(BUTTON_PIN, INPUT_PULLUP);

    // SPI 시작
    SPI.begin();

    // RC522 초기화
    mfrc522.PCD_Init();

    // 기본 Key A = FF FF FF FF FF FF
    for (byte i = 0; i < 6; i++) {
        key.keyByte[i] = 0xFF;
    }

    Serial.println(F("================================"));
    Serial.println(F("RC522 RFID Reader / Writer"));
    Serial.println(F("================================"));
    Serial.println(F("READ mode"));
    Serial.println(F("Scan a MIFARE Classic card."));
}


// ========================================
// 메인 루프
// ========================================

void loop() {

    // 새로운 카드가 없으면 대기
    if (!mfrc522.PICC_IsNewCardPresent()) {
        return;
    }

    // 카드 선택
    if (!mfrc522.PICC_ReadCardSerial()) {
        return;
    }


    // 카드 정보 출력
    printCardInfo();


    // 버튼 상태에 따라 동작
    if (digitalRead(BUTTON_PIN) == LOW) {

        // ================================
        // WRITE MODE
        // ================================

        Serial.println();
        Serial.println(F("[WRITE MODE]"));

        if (!dataReady) {
            Serial.println(
                F("No card data has been stored yet.")
            );
        }
        else {
            writeCard();
        }

    }
    else {

        // ================================
        // READ MODE
        // ================================

        Serial.println();
        Serial.println(F("[READ MODE]"));

        readCard();
    }


    // 카드 통신 종료
    mfrc522.PICC_HaltA();
    mfrc522.PCD_StopCrypto1();

    Serial.println();
    Serial.println(F("-------------------------------"));
    delay(1000);
}


// ========================================
// 카드 정보 출력
// ========================================

void printCardInfo() {

    MFRC522::PICC_Type piccType;

    piccType = mfrc522.PICC_GetType(
        mfrc522.uid.sak
    );


    Serial.println();
    Serial.println(F("===== CARD INFO ====="));

    // 카드 종류
    Serial.print(F("Type : "));
    Serial.println(
        mfrc522.PICC_GetTypeName(piccType)
    );


    // SAK
    Serial.print(F("SAK  : 0x"));

    if (mfrc522.uid.sak < 0x10) {
        Serial.print("0");
    }

    Serial.println(
        mfrc522.uid.sak,
        HEX
    );


    // UID
    Serial.print(F("UID  : "));

    for (byte i = 0; i < mfrc522.uid.size; i++) {

        if (mfrc522.uid.uidByte[i] < 0x10) {
            Serial.print("0");
        }

        Serial.print(
            mfrc522.uid.uidByte[i],
            HEX
        );

        Serial.print(" ");
    }

    Serial.println();


    // UID 길이
    Serial.print(F("UID size : "));
    Serial.print(mfrc522.uid.size);
    Serial.println(F(" bytes"));

    Serial.println(F("====================="));
}


// ========================================
// 카드 읽기
// ========================================

void readCard() {

    MFRC522::PICC_Type piccType;

    piccType = mfrc522.PICC_GetType(
        mfrc522.uid.sak
    );


    // MIFARE Classic인지 확인
    if (piccType != MFRC522::PICC_TYPE_MIFARE_MINI &&
        piccType != MFRC522::PICC_TYPE_MIFARE_1K &&
        piccType != MFRC522::PICC_TYPE_MIFARE_4K) {

        Serial.println(
            F("This card is not a MIFARE Classic card.")
        );

        return;
    }


    // MIFARE Classic 1K만 처리
    if (piccType != MFRC522::PICC_TYPE_MIFARE_1K) {

        Serial.println(
            F("This example supports MIFARE Classic 1K.")
        );

        return;
    }


    Serial.println();
    Serial.println(F("Reading card..."));


    bool success = true;


    // MIFARE Classic 1K
    // 64 blocks = 16 sectors × 4 blocks
    for (byte block = 0; block < TOTAL_BLOCKS; block++) {

        // 블록 0은 UID/제조사 영역이므로 읽기만 함
        // 섹터 트레일러(3, 7, 11, ...)는
        // 인증키와 접근조건을 포함하므로 저장하지 않음
        if (block % 4 == 3) {
            continue;
        }


        // 해당 섹터 인증
        if (!authenticateBlock(block)) {

            Serial.print(F("Authentication failed: block "));
            Serial.println(block);

            success = false;
            continue;
        }


        byte buffer[18];
        byte size = sizeof(buffer);


        // 블록 읽기
        MFRC522::StatusCode status;

        status = mfrc522.MIFARE_Read(
            block,
            buffer,
            &size
        );


        if (status != MFRC522::STATUS_OK) {

            Serial.print(F("Read failed: block "));
            Serial.println(block);

            success = false;
            continue;
        }


        // 읽은 데이터 저장
        for (byte i = 0; i < BLOCK_SIZE; i++) {
            cardData[block][i] = buffer[i];
        }


        // 시리얼 모니터에 출력
        printBlock(block, buffer);
    }


    mfrc522.PCD_StopCrypto1();


    if (success) {

        dataReady = true;

        Serial.println();
        Serial.println(
            F("Card data stored in memory.")
        );

        Serial.println(
            F("Press the button and scan another card to write.")
        );
    }
    else {

        Serial.println();
        Serial.println(
            F("Some blocks could not be read.")
        );
    }
}


// ========================================
// 카드 쓰기
// ========================================

void writeCard() {

    MFRC522::PICC_Type piccType;

    piccType = mfrc522.PICC_GetType(
        mfrc522.uid.sak
    );


    // MIFARE Classic 1K인지 확인
    if (piccType != MFRC522::PICC_TYPE_MIFARE_1K) {

        Serial.println(
            F("Target card is not MIFARE Classic 1K.")
        );

        return;
    }


    Serial.println();
    Serial.println(
        F("Writing stored data to card...")
    );


    bool success = true;


    for (byte block = 0; block < TOTAL_BLOCKS; block++) {

        // --------------------------------
        // 쓰면 안 되는 영역
        // --------------------------------

        // Block 0:
        // UID + 제조사 정보
        if (block == 0) {
            continue;
        }


        // Sector Trailer:
        // 3, 7, 11, 15, ...
        //
        // Key A
        // Access Bits
        // Key B
        //
        // 는 복사하지 않는다.
        if (block % 4 == 3) {
            continue;
        }


        // 해당 섹터 인증
        if (!authenticateBlock(block)) {

            Serial.print(
                F("Authentication failed: block ")
            );

            Serial.println(block);

            success = false;
            continue;
        }


        // 16바이트 데이터 기록
        MFRC522::StatusCode status;

        status = mfrc522.MIFARE_Write(
            block,
            cardData[block],
            BLOCK_SIZE
        );


        if (status != MFRC522::STATUS_OK) {

            Serial.print(
                F("Write failed: block ")
            );

            Serial.println(block);

            success = false;
            continue;
        }


        Serial.print(F("Written block "));
        Serial.println(block);
    }


    mfrc522.PCD_StopCrypto1();


    Serial.println();

    if (success) {
        Serial.println(
            F("Write completed.")
        );
    }
    else {
        Serial.println(
            F("Write completed with errors.")
        );
    }
}


// ========================================
// MIFARE 블록 인증
// ========================================

bool authenticateBlock(byte block) {

    MFRC522::StatusCode status;

    status = mfrc522.PCD_Authenticate(
        MFRC522::PICC_CMD_MF_AUTH_KEY_A,
        block,
        &key,
        &(mfrc522.uid)
    );


    if (status != MFRC522::STATUS_OK) {

        Serial.print(F("Authentication error: "));
        Serial.println(
            mfrc522.GetStatusCodeName(status)
        );

        return false;
    }

    return true;
}


// ========================================
// 블록 데이터 출력
// ========================================

void printBlock(
    byte block,
    byte *buffer
) {

    Serial.print(F("Block "));
    
    if (block < 10) {
        Serial.print(" ");
    }

    Serial.print(block);
    Serial.print(F(" : "));


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

        if (buffer[i] < 0x10) {
            Serial.print("0");
        }

        Serial.print(
            buffer[i],
            HEX
        );

        Serial.print(" ");
    }

    Serial.println();
}
```