안녕하세요. 애드라이프입니다.
이전 포스팅에서 RFID-RC522 모듈을 이용해서
간단하게 RFID TAG의 정보를 읽어보는 방법을
알아봤습니다. 이번에는 예고한 대로
읽는 법 이외에 쓰기와 이론을 좀 더 알아보도록 하겠습니다.!!
이전 포스팅이 궁금하신 분들은 아래 링크 참고해주세요.
[아두이노/아두이노 중급] - 아두이노(Arduino) 중급 RFID - RC522 모듈로 NFC 사용하기 #1
이번 포스팅에서는 이전 포스팅에서
사용했던 백색 RFID CARD와
제 회사 사원증을 가지고 여러 가지 TEST를
해보겠습니다!!
사원증의 정보가 바뀌면 출퇴근을 못하니
상당히 조심스럽게 진행하겠습니다. ㅠㅠ
웬만한 학생증, 사원증, 출입증 모두 같은
방식이니 TEST 해보시는 것도 괜찮을 거 같습니다!!
다만, 쓰기 예제 TEST시에는 주의할 것!!
자! 이번에 읽기, 쓰기를 TEST 하기 전에 알아야 할
이론을 먼저 언급하고 진행하겠습니다!!
이전 포스팅에서 간단히 설명했지만
우리가 사용하고자 하는 TAG는
총 64개의 블록이 존재합니다.
그리고 블록 4개씩 묶어 하나의 Sector를 구성하죠
그리고 Sector의 마지막 블록은 키 데이터를
지니고 있습니다. 그럼 총 15개 Sector에 64개 블록이네요!
그림으로 표현하였습니다.
좀 더 이해하기 쉬우신가요?
위 그림과 같은 구조로 되어있는 RFID CARD를
읽고 쓰기 위해서는 Sector의 마지막 블록인
Trailer의 설정으로 결정됩니다.
처음에는 KeyA를 읽고 데이터를 쓰지만
AccessCondtions를 수정하여 KeyB로
같은 역할을 수행할 수 있습니다.
프로그램을 보면서 나머지 설명을 이어가겠습니다.
/*
MFRC522 라이브러리를 이용하여,
[프로그램 수순]
- 기본 A 키로 인증을 합니다
- 기본 A 키, 새로운 B 키, 그리고 액세스 비트(Access Bits) 값들을
섹터 트레일러(Sector Trailer)에 등록하여 B 키로만 데이터와
섹터 트레일러를 읽고 쓸 수 있게 만듭니다
- B 키 값으로 인증을 하고
- 블록에 새로운 데이터 값을 기록하고
- 블록을 읽어 데이터가 잘 기록되었는지 확인하고
- B 키 값을 기본 값인 FF FF FF FF FF FF로 복원하고, A 키 값을
이용하게 액세스 비트(Access Bits) C0 C1 C2 값을 0 0 1로 설정합니다
*/
#include <SPI.h>
#include <MFRC522.h>
#define SS_PIN 10
#define RST_PIN 9
const byte KEYB[] = {
0xDE, 0xAD, 0xBE, 0xEB, 0xDE, 0xAD };
MFRC522 mfrc522(SS_PIN, RST_PIN); // MFRC522 인스턴스 생성
void setup() {
Serial.begin(9600); // PC와 시리얼 통신을 위한 초기화를 합니다
SPI.begin(); // SPI 버스 사용을 위한 초기화를 합니다
mfrc522.PCD_Init(); // MFRC522를 초기화 합니다.
Serial.println("B 키를 이용하여 데이터를 쓰고 읽는 방법을 "
"보여주는 예제입니다:");
}
void loop() {
byte buffer[18];
byte size = sizeof(buffer);
// 새로운 카드가 접촉되었는지 확인합니다:
if ( ! mfrc522.PICC_IsNewCardPresent()) {
return;
}
// 카드들 중 하나를 선택합니다:
if ( ! mfrc522.PICC_ReadCardSerial()) {
return;
}
// 카드가 선택되었고, mfrc522.uid에 UID와 SAK 정보가 들어 있습니다.
// UID 정보를 16 진수 형태로 출력:
Serial.print("카드 UID:");
for (byte i = 0; i < mfrc522.uid.size; i++) {
Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
Serial.print(mfrc522.uid.uidByte[i], HEX);
}
Serial.println();
// PICC 타입 출력:
byte piccType = mfrc522.PICC_GetType(mfrc522.uid.sak);
Serial.print("PICC 타입: ");
Serial.println(mfrc522.PICC_GetTypeName(piccType));
if ( piccType != MFRC522::PICC_TYPE_MIFARE_MINI
&& piccType != MFRC522::PICC_TYPE_MIFARE_1K
&& piccType != MFRC522::PICC_TYPE_MIFARE_4K) {
Serial.println("이 예제는 MIFARE Classic 카드에서만 동작합니다.");
return;
}
// keyA는 기본값으로 keyB는 새로 사용할 key로 준비합니다 여기에서는
// 상수로 선언되어 있는 KEYB[]를 사용합니다. 다른 키를 사용하려면,
// KEYB[] 값을 수정하여 사용하기 바랍니다:
MFRC522::MIFARE_Key keyA, keyB;
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
byte trailerBuffer[] = { 255,255,255,255,255,255, 0,0,0,0, 255,255,255,255,255,255 }; // Keep default keys.
for (byte i = 0; i < 6; i++) {
keyA.keyByte[i] = 0xFF;
keyB.keyByte[i] = trailerBuffer[i + 10] = KEYB[i];
}
// 이 예제에서는 2번째 섹터의 4 번과 7 번 블록(Sector Trailer)을
// 사용합니다.
byte valueBlockA = 4;
byte trailerBlock = 7;
// A 키로 인증을 합니다:
Serial.println("A 키로 인증 중...");
byte status;
status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &keyA, &(mfrc522.uid));
if (status != MFRC522::STATUS_OK) {
Serial.print("PCD_Authenticate() 실패: ");
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
// B 키를 사용하기 위하여 섹터의 마지막 블록(Sector Trailer)에
// B 키 값과 Access Bits C0 C1 C2 값을 0 1 1로 설정합니다:
mfrc522.MIFARE_SetAccessBits(&trailerBuffer[6], 3, 3, 3, 3);
Serial.println("섹터 트레일러 기록 중...");
status = mfrc522.MIFARE_Write(trailerBlock, trailerBuffer, 16);
if (status != MFRC522::STATUS_OK) {
Serial.print("MIFARE_Write() 실패: ");
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
// 새로 등록한 B 키로 인증을 합니다:
Serial.println("B 키로 인증 중...");
status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &keyB, &(mfrc522.uid));
if (status != MFRC522::STATUS_OK) {
Serial.print("PCD_Authenticate() 실패: ");
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
byte value1Block[] = {
// 1,2,3,4, 5,6,7,8, 9,10,255,12, 13,14,15,16, valueBlockA,~valueBlockA,valueBlockA,~valueBlockA };
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, valueBlockA,~valueBlockA,valueBlockA,~valueBlockA };
//0x22, 0xA9, 0x75, 0x1D, 0xE3, 0x08, 0x04, 0x00, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, valueBlockA,~valueBlockA,valueBlockA,~valueBlockA };
// 4 번째 블록(value block)에 데이터를 기록합니다:
Serial.println("4 번째 블록 기록 중: 2 번째 섹터의 첫번째 블록");
print_buffer(value1Block);
status = mfrc522.MIFARE_Write(valueBlockA, value1Block, 16);
if (status != MFRC522::STATUS_OK) {
Serial.print("MIFARE_Write() 실패: ");
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
Serial.println("4 번째 블록 읽는 중: 2 번째 섹터의 첫번째 불록");
size = sizeof(buffer);
status = mfrc522.MIFARE_Read(valueBlockA, buffer, &size);
if (status != MFRC522::STATUS_OK) {
Serial.print("MIFARE_Read() 실패: ");
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
print_buffer(buffer);
Serial.print("4 번째 블록 읽기 : 2 번째 섹터의 첫번째 블록 : ");
//byte value1Block[] = { 1,2,3,4, 5,6,7,8, 9,10,255,12, 13,14,15,16, valueBlockA,~valueBlockA,valueBlockA,~valueBlockA };
if (
buffer[0] == 1 && buffer[1] == 2 &&
buffer[2] == 3 && buffer[3] == 4 &&
buffer[4] == 5 && buffer[5] == 6 &&
buffer[6] == 7 && buffer[7] == 8 &&
buffer[8] == 9 && buffer[9] == 10 &&
buffer[10] == 255 && buffer[11] == 12 &&
buffer[12] == 13 && buffer[13] == 14 &&
buffer[14] == 15 && buffer[15] == 16
){
Serial.println("성공");
}
else{
Serial.println("실패");
}
// B 키를 기본 값인 FFFFFFFFFFFF로 복원합니다:
for (byte i = 0; i < 6; i++) {
keyA.keyByte[i] = 0xFF;
trailerBuffer[i + 10] = 0xFF;
}
// 또한 액세스 비트(Access Bits) C0 C1 C2 값을 0 0 1로 하여
// Sector Trailer를 A 키로 액세스하게 만듭니다:
mfrc522.MIFARE_SetAccessBits(&trailerBuffer[6], 0, 0, 0, 1);
Serial.println("섹터 트레일러 기록 중...");
status = mfrc522.MIFARE_Write(trailerBlock, trailerBuffer, 16);
if (status != MFRC522::STATUS_OK) {
Serial.print("MIFARE_Write() 실패: ");
Serial.println(mfrc522.GetStatusCodeName(status));
} else {
Serial.println("섹터 트레일러 기록 완료");
}
// PICC를 멈춥니다
mfrc522.PICC_HaltA();
// PCD의 Crypto를 멈춥니다
mfrc522.PCD_StopCrypto1();
// 스케치도 멈추게 합니다:
while(true) {
;
}
}
void print_buffer(byte buffer[])
{
Serial.print(String("buffer[] = {"));
for(int i = 0; i < 16; i++) {
if(i != 0)
Serial.print(',');
Serial.print(buffer[i]);
}
Serial.println("}");
}
우선 가장 처음에 사용하고자 하는 RFID TAG의
64개 블록 데이터를 모두 읽어 확인합니다.
그래야 내가 원하는 곳을 바꿨을 때 비교할 수 있으니까요.
데이터 전부를 읽는 방법은 이전 포스팅에서
자세히 다뤘으니 꼭 참고 바랍니다!!
그럼 바로 읽은 결과를 보겠습니다.
Card UID: 22 A9 75 1D
Card SAK: 08
PICC type: MIFARE 1KB
Sector Block 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 AccessBits
15 63 00 00 00 00 00 00 FF 07 80 69 FF FF FF FF FF FF [ 0 0 1 ]
62 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
61 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
14 59 00 00 00 00 00 00 FF 07 80 69 FF FF FF FF FF FF [ 0 0 1 ]
58 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
57 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
56 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
13 55 00 00 00 00 00 00 FF 07 80 69 FF FF FF FF FF FF [ 0 0 1 ]
54 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
53 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
52 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
12 51 00 00 00 00 00 00 FF 07 80 69 FF FF FF FF FF FF [ 0 0 1 ]
50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
49 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
48 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
11 47 00 00 00 00 00 00 FF 07 80 69 FF FF FF FF FF FF [ 0 0 1 ]
46 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
45 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
44 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
10 43 00 00 00 00 00 00 FF 07 80 69 FF FF FF FF FF FF [ 0 0 1 ]
42 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
41 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
9 39 00 00 00 00 00 00 FF 07 80 69 FF FF FF FF FF FF [ 0 0 1 ]
38 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
37 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
36 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
8 35 00 00 00 00 00 00 FF 07 80 69 FF FF FF FF FF FF [ 0 0 1 ]
34 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
7 31 00 00 00 00 00 00 FF 07 80 69 FF FF FF FF FF FF [ 0 0 1 ]
30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
29 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
28 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
6 27 00 00 00 00 00 00 FF 07 80 69 FF FF FF FF FF FF [ 0 0 1 ]
26 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
25 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
24 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
5 23 00 00 00 00 00 00 FF 07 80 69 FF FF FF FF FF FF [ 0 0 1 ]
22 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
21 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
4 19 00 00 00 00 00 00 FF 07 80 69 FF FF FF FF FF FF [ 0 0 1 ]
18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
17 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
16 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
3 15 00 00 00 00 00 00 FF 07 80 69 FF FF FF FF FF FF [ 0 0 1 ]
14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
13 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
12 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
2 11 00 00 00 00 00 00 FF 07 80 69 FF FF FF FF FF FF [ 0 0 1 ]
10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
9 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
1 7 00 00 00 00 00 00 FF 07 80 00 FF FF FF FF FF FF [ 0 0 1 ]
6 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
5 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
0 3 00 00 00 00 00 00 FF 07 80 69 FF FF FF FF FF FF [ 0 0 1 ]
2 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 0 0 0 ]
0 22 A9 75 1D E3 08 04 00 62 63 64 65 66 67 68 69 [ 0 0 0 ]
읽은 데이터를 확인해 보니 0번째 블록에 데이터가
쓰여있고 다른 곳은 다 비슷비슷하니 데이터가
없는 것 같습니다. 진하게 표시한 부분이 해당 카드의 UID입니다.
위 프로그램은 4번째 블록의 데이터를 바꾸는
역할을 수행합니다. 그러기 위해서 1 Sector의 Trailer인
7번 블록을 활용하고 있습니다.
프로그램에서 아래와 같이 정의되어있는 곳을
변경하기만 하면 자신이 원하는 블록을 변경할 수 있습니다.
byte valueBlockA = 4;
byte trailerBlock = 7;
하지만 데이터를 쓰고자 하는 블록의
Trailer를 맞게 써죠야 겠죠?
예를 들어 1번 블록의 데이터를 변경하려면
trailerBlock = 3; 이 되어야 합니다.
그리고 자신이 원하는 데이터를 쓰는 곳은
byte value1Block[] = {
// 1,2,3,4, 5,6,7,8, 9,10,255,12, 13,14,15,16, valueBlockA,~valueBlockA,valueBlockA,~valueBlockA };
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, valueBlockA,~valueBlockA,valueBlockA,~valueBlockA };
//0x22, 0xA9, 0x75, 0x1D, 0xE3, 0x08, 0x04, 0x00, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, valueBlockA,~valueBlockA,valueBlockA,~valueBlockA };
이곳을 수정하면 수정한 대로 데이터가 입력됩니다.
예시로 초기화하는 0만 들어가는 부분과
Hex코드 부분을 주석으로 추가하였으니 활용하시면 됩니다.
마지막으로 변경한 데이터가 제대로
변경되었는지 확인하는 부분이
//byte value1Block[] = { 1,2,3,4, 5,6,7,8, 9,10,255,12, 13,14,15,16, valueBlockA,~valueBlockA,valueBlockA,~valueBlockA };
if (
buffer[0] == 1 && buffer[1] == 2 &&
buffer[2] == 3 && buffer[3] == 4 &&
buffer[4] == 5 && buffer[5] == 6 &&
buffer[6] == 7 && buffer[7] == 8 &&
buffer[8] == 9 && buffer[9] == 10 &&
buffer[10] == 255 && buffer[11] == 12 &&
buffer[12] == 13 && buffer[13] == 14 &&
buffer[14] == 15 && buffer[15] == 16
){
Serial.println("성공");
}
else{
Serial.println("실패");
}
이 곳에서 수정한 데이터와 같이
해당 버퍼의 값을 변경해주면
같을 때 "성공" 다르면 "실패"라는
문구를 시리얼 모니터에 출력하게 됩니다.
그럼 프로그램을 실행시켜 정말로
원하는 곳에 데이터가 수정되고
비교까지 되는지 확인해보겠습니다.
결과를 보면 원하는 데이터를 잘 썼고
쓴 데이터를 읽어 봤더니 제대로 쓰였다.
라고 출력이 되네요.
바로 실제 카드의 데이터를 확인해 보겠습니다.
4번 블록의 데이터가 변경된 것이 보이나요?
프로그램에서 설정한 대로 변경되어 있네요.
이렇게 프로그램이 문제없이 동작하는 것을
확인할 수 있었습니다. 여기서 지금 사용하고 있는
RFID TAG랑 거의 같은 모양의 제 사원증을
읽어 보고 싶어서 읽어 보았습니다.
읽기 전에는 뭔가 보안이 철저하고 엄청난
데이터가 쓰여있지 않을까 내심 기대했습니다만
정말 텅텅 비어있더군요.
뭔가 다르면 포스팅해볼까 했지만.....
현재 사용하고 있는 RFID TAG랑 똑같이
0번째 블록(UID부분)의 데이터만 조금 다르고
나머지는 전부 비어있었습니다.
단순히 카드별로 ID만 있고
카드리더기는 그것을 식별하는 수준인 것 같네요.
대략 실망.....
실망감을 감추지 못하면서 이번 포스팅을 마무리합니다.
다음에는 더욱 유익한 포스팅으로 돌아오겠습니다.
by 애드라이프
'아두이노 > 아두이노 중급' 카테고리의 다른 글
아두이노(Arduino) 중급 RFID - RC522 모듈로 NFC사용하기 #1 (2) | 2020.05.02 |
---|---|
아두이노(Arduino) 중급 WIFI모듈 NodeMCU(ESP8266) - LED제어(Blynk) (0) | 2018.12.15 |
아두이노(Arduino) 중급 WIFI모듈 WeMos D1 R2 - LED제어(Blynk) (0) | 2018.12.14 |
아두이노(Arduino) 중급 WIFI모듈 WeMos D1 Mini 3편 - LED제어 (0) | 2018.12.11 |
아두이노(Arduino) 중급 WIFI모듈 WeMos D1 Mini 2편 - Blynk설치 (0) | 2018.12.10 |