Skip to content

northpard/RFID-Card

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 

Repository files navigation

RFID 카드 읽기/쓰기 (Arduino + MFRC522)

이 스케치는 MFRC522 리더를 이용해 MIFARE Classic 카드에 문자열, 정수, 구조체 등 여러 타입의 데이터를 쓰고/읽는 예제입니다. 학습 목적으로 각 단계(챕터)의 코드를 주석으로 보존하며, 변경점에는 [CH2] ~ [CH7] 태그를 달아 차이를 쉽게 확인할 수 있습니다.

구성 요소

  • 보드: Arduino (UNO 기준, 다른 보드도 SPI 핀만 맞추면 가능)
  • 리더: MFRC522 (RC522)
  • 라이브러리: MFRC522, SPI

배선

  • SS(SDA) → D10
  • RST → D9
  • 나머지 SCK/MOSI/MISO는 보드의 하드웨어 SPI 핀 사용

시리얼 명령어 (9600 baud)

  • ws 문자열 쓰기: 블록 60에 기본 문자열 기록
  • wi 정수 쓰기: 블록 61에 16비트 정수(예: 32767) 기록
  • rs 문자열 읽기: 블록 60에서 읽기
  • ri 정수 읽기: 블록 61에서 읽기
  • wt 구조체 쓰기: TagData를 블록 56–57(연속 두 블록)에 기록
  • rt 구조체 읽기: 블록 56–57에서 TagData 읽기 후 항목별 출력

챕터 구성 (코드 내 주석으로 구분)

  • CH2: 기본 쓰기 흐름
  • CH3: checkAuth, writeString 헬퍼 추가 및 리팩터링
  • CH4: 문자열 읽기 readString
  • CH5: 정수 쓰기 writeInteger
  • CH6: 정수 읽기 readInteger
  • CH7: 구조체 TagData(name/total/payment) 2블록 단위 읽기/쓰기

챕터별 상세 변경

  • CH2
    • 목적: RC522 태그에 문자열을 직접 쓰는 최소 동작을 확인.
    • 주요 변경: loop()에서 w 명령을 직접 처리하고, 블록 60 인증 후 고정 문자열을 MIFARE_Write로 기록.
      if (!rc522.PICC_IsNewCardPresent()) return;
      if (!rc522.PICC_ReadCardSerial()) return;
      status = rc522.PCD_Authenticate(..., 60, &key, &(rc522.uid));
      String name = "clyde's";
      name.toCharArray(data, name.length() + 1);
      status = rc522.MIFARE_Write(60, (byte*)&data, 16);
  • CH3
    • 목적: 중복 코드를 줄이고 재사용 가능한 기본 구조를 마련.
    • 주요 변경: checkAuth, writeString 헬퍼를 추가하고, switch 기반 명령 처리로 loop()를 리팩터링.
      switch (cmd.charAt(0)) {
        case 'w':
          status = writeString(60, key, "clyde's");
          break;
      }
      
      MFRC522::StatusCode checkAuth(int index, MFRC522::MIFARE_Key key) {
        return rc522.PCD_Authenticate(PICC_CMD_MF_AUTH_KEY_A, index, &key, &(rc522.uid));
      }
  • CH4
    • 목적: 저장된 문자열을 읽어 검증할 수 있는 기능 추가.
    • 주요 변경: readString 함수 도입, r 명령 및 rs 서브커맨드를 통해 블록 60을 읽고 시리얼에 출력.
      case 'r':
        switch (cmd.charAt(1)) {
          case 's':
            status = readString(60, key, s_data);
            Serial.println(s_data);
            break;
        }
      
      MFRC522::StatusCode readString(int index, MFRC522::MIFARE_Key key, String& data) {
        status = checkAuth(index, key);
        byte buffer[18], length = 18;
        status = rc522.MIFARE_Read(index, buffer, &length);
        data = String((char*)buffer);
        return status;
      }
  • CH5
    • 목적: 문자열뿐 아니라 정수 데이터도 카드에 저장·조회 가능하도록 확장.
    • 주요 변경: writeInteger/readInteger, toBytes/toInteger 함수 추가, wi·ri 명령으로 블록 61에 16비트 정수를 Little Endian으로 입·출력.
      case 'w':
        if (cmd.charAt(1) == 'i') {
          status = writeInteger(61, key, 32767);
          rc522.PICC_DumpToSerial(&(rc522.uid));
        }
        break;
      case 'r':
        if (cmd.charAt(1) == 'i') {
          status = readInteger(61, key, i_data);
          Serial.println(i_data); // 32767 출력
        }
        break;
      
      MFRC522::StatusCode writeInteger(int index, MFRC522::MIFARE_Key key, int data) {
        MFRC522::StatusCode status = checkAuth(index, key);
        byte buffer[16]; memset(buffer, 0x00, sizeof(buffer));
        toBytes(buffer, data); // LSB-first 로 2바이트 채움
        return rc522.MIFARE_Write(index, buffer, sizeof(buffer));
      }
  • CH6
    • 목적: 읽기 명령을 분리·보강하고 임시 버퍼 관리를 개선.
    • 주요 변경: loop()에서 rs/ri 처리 시 임시 변수(s_data, i_data)를 사용하고, readInteger/readString 성공 시 값을 시리얼에 출력.
      case 'r':
        switch (cmd.charAt(1)) {
          case 's':
            status = readString(60, key, s_data);
            Serial.println(s_data);
            break;
          case 'i':
            status = readInteger(61, key, i_data);
            Serial.println(i_data);
            break;
        }
  • CH7
    • 목적: 구조체 형태의 복합 데이터를 두 블록에 나누어 저장·조회하는 고급 예제 구현.
    • 주요 변경: TagData 구조체 정의, writeTagData/readTagData 추가, wt·rt 명령이 블록 56–57에 name/total/payment를 연속 기록하고 다시 읽어 필드별로 출력.

주요 헬퍼 함수

  • checkAuth : 지정 블록에 접근하기 전에 기본 키 A로 인증을 수행하고 실패 시 오류 메시지를 출력.
  • toBytes / toInteger : 16비트 정수를 리틀엔디언으로 버퍼에 변환하거나 다시 정수로 복원.
  • writeString / readString : 문자열을 16바이트 블록에 기록하거나 해당 블록에서 읽어 String으로 반환.
  • writeInteger / readInteger : 정수를 16바이트 버퍼(나머지 0 패딩)에 써 넣고 다시 읽어 복원.
  • writeTagData / readTagData : TagData(name, total, payment) 구조체를 32바이트 버퍼에 담아 연속 두 블록에 기록하고 다시 읽어 필드별로 복원.

사용 방법

  1. Arduino IDE에서 라이브러리(MFRC522) 설치
  2. 보드와 포트 선택 후 스케치 업로드
  3. 시리얼 모니터(9600, 줄바꿈 전송)에서 명령 입력
    • 카드/태그를 리더에 올려놓은 상태에서 ws, wi, rs, ri, wt, rt 실행

참고 사항

  • 블록 인덱스(56, 57, 60, 61)는 예시이므로 카드 레이아웃에 맞게 조정 가능합니다.
  • 보안 키는 기본값(0xFF)으로 동작하도록 되어 있습니다. 실제 서비스 환경에서는 키를 변경해주세요.
  • 카드 종류가 MIFARE Classic이 아닌 경우 동작이 다를 수 있습니다.

데이터 저장 블록 요약

블록 용도 관련 명령 기본 데이터 예시
56 TagData 앞 16B wt / rt 이름/총액 일부
57 TagData 뒤 16B wt / rt 결제 금액 등 나머지
60 문자열 ws / rs "clyde's"
61 16비트 정수 wi / ri 32767FF 7F

기초 지식: RFID와 MFRC522

  • RFID란?
    • 전자기장(13.56MHz, HF)을 이용해 태그와 리더가 비접촉으로 통신하는 기술입니다.
    • 태그는 전원(배터리)이 없는 수동형이 보편적이며, 리더의 전자기장으로부터 전력을 유도해 응답합니다.
  • MFRC522 리더 칩
    • SPI/I2C/UART를 지원하는 13.56MHz용 리더 IC로, MIFARE Classic(1K/4K), Ultralight 등 ISO/IEC 14443 Type A 태그를 지원합니다.
    • Arduino 모듈(일명 RC522 보드)은 주로 SPI를 사용하며, SDA(SS), SCK, MOSI, MISO, RST, 3.3V, GND 핀을 제공합니다.
  • MIFARE Classic 카드
    • 제품군: 1K(16섹터, 64블록)과 4K(40섹터, 256블록) 변형이 가장 흔하며, 각 블록은 16바이트 고정 길이를 갖습니다.
    • 메모리 구성: 각 섹터의 마지막 블록은 섹터 트레일러로, 키 A/B와 접근 비트를 저장합니다. 접근 비트는 섹터 내 다른 블록에 대한 읽기/쓰기 권한을 정의하므로 임의로 덮어쓰면 섹터 전체가 잠길 수 있습니다.
    • 인증 흐름: 리더는 UID를 읽은 뒤, 원하는 섹터의 트레일러에 저장된 키 A 또는 키 B 중 하나로 인증해야 데이터 블록에 접근할 수 있습니다. 이 스케치는 학습 목적상 기본 키(FF FF FF FF FF FF)를 사용합니다.
    • 데이터 저장 팁: 데이터 블록은 16바이트 단위이므로, 문자열은 널 종료 문자를 고려해 길이를 조정하고, 정수/구조체는 엔디안을 통일해야 합니다. 또한 한 섹터의 마지막 블록은 데이터 저장에 사용하지 않는 것이 안전합니다.
  • MIFARE Classic 동작 예시
    • 스케치에서는 블록 56~57에 구조체(TagData)를 저장하고, 블록 60·61에 문자열/정수를 개별로 저장합니다.
    • write* 명령을 수행하면 인증 → 데이터 블록 접근 → 쓰기 순서로 진행되며, 이후 read* 명령으로 동일 블록을 읽어 원복 여부를 확인합니다.
  • MIFARE Classic 메모리 구조(요약)
    • 블록 크기: 16바이트. 섹터는 여러 블록으로 구성됩니다(1K 카드 기준 섹터 0~15, 각 섹터 4블록 = 총 64블록).
    • 섹터 트레일러: 각 섹터의 마지막 블록은 키A/키B와 접근 비트가 저장되는 특수 블록입니다. 일반 데이터 저장 금지.
    • 인증: 데이터 블록 접근 전 키A 또는 키B로 인증이 필요합니다(본 예제는 기본 키 FF FF FF FF FF FF 사용).
  • UID(고유번호)
    • 카드의 고유 식별자로, 안티콜리전 과정을 통해 읽어옵니다. 변경 불가(일부 비정상 카드 제외).
  • 데이터 저장 팁
    • 16바이트 정렬: 한 블록은 16바이트이므로, 문자열은 널 종료 포함 길이 관리가 필요합니다.
    • 엔디안: 본 예제는 정수 2바이트를 리틀엔디언으로 인코딩/디코딩합니다(toBytes, toInteger).
    • 민감 정보: 키·개인정보는 카드에 평문 저장하지 않도록 주의하세요. 필요 시 암호화/서명 적용을 고려하세요.
  • 제한 사항 및 주의
    • 보안 취약점: MIFARE Classic은 알려진 취약점이 있으므로 고보안 용도에는 적합하지 않습니다.
    • 전원/거리: RC522는 3.3V 구동이며, 안테나 설계/환경에 따라 인식 거리가 달라집니다(수 cm 수준).
    • 권한 비트 손상 주의: 섹터 트레일러를 잘못 쓰면 섹터가 영구 잠길 수 있으니, 트레일러 블록은 건드리지 마세요.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages