ACR122U(Linux)를 사용하여 C/C++ 응용 프로그램 만들기
이 강좌를 배우려면 다음이 필요합니다.
1. 드라이버 설치
첫 번째 단계는 ACR122U의 드라이버를 설치하는 것입니다.우리가 만들 프로그램을 개발하고 실행하려면 이 절차가 필요합니다.
다운로드 부분의 official website을 살펴보겠습니다.
Linux는 두 가지 다운로드가 있습니다.
PC/SC Driver Package에는 다양한 릴리즈(Debian, Fedora, Ubuntu, Raspbian...)에 사용할 수 있는 패키지가 포함되어 있습니다.만약 이 발행판 중 하나를 사용한다면, 이것은 드라이버를 설치하는 가장 간단한 방법입니다.
PC/SC Drivers 드라이버의 소스 코드를 포함합니다.설치 전에 드라이버를 구축해야 하기 때문에, 이 드라이버는 어떤 리눅스 버전에서도 작동할 수 있기 때문에 좀 복잡하다.
wget https://www.acs.com.hk/download-driver-unified/12030/ACS-Unified-Driver-Lnx-Mac-118-P.zip
unzip ACS-Unified-Driver-Lnx-Mac-118-P.zip
cd ACS-Unified-Driver-Lnx-Mac-118-P/
README
파일은 소스를 구축하는 데 필요한 내용을 나타냅니다.# Fedora
sudo dnf install pcsc-lite libusb flex perl pkg-config pcsc-lite-devel libusb-devel
# Ubuntu
sudo apt-get install pcscd libpcsclite1 libusb-1.0-0 flex perl pkg-config libpcsclite-dev libusb-1.0-0-dev
그런 다음 소스 코드의 압축을 풀고 구성, 구축 및 설치를 수행할 수 있습니다.tar -xf acsccid-1.1.8.tar.bz2
cd acsccid-1.1.8/
./configure
./configure --disable-dependency-tracking # for Ubuntu
make
sudo make install
두 단계가 더 필요합니다.먼저 pcscd
서비스가 실행 중인지 확인해야 합니다.sudo systemctl start pcscd
sudo systemctl enable pcscd
그런 다음 다음 다음 명령을 실행해야 합니다.echo "blacklist pn533_usb" | sudo tee -a /etc/modprobe.d/blacklist-libnfc.conf
이것은 드라이버와 기본 핵 모듈 사이의 충돌을 피하는 데 사용됩니다.이제 컴퓨터를 다시 시작할 수 있다.
만약 당신이 Yocto를 사용한다면, 나는 식단 here을 만들었다.
2. SDK 설치
다음 단계는 응용 프로그램 개발에 필요한 SDK를 설치하는 것이지만, 응용 프로그램을 실행하는 데 필요한 것은 아니다.
공식 웹사이트에서 제안한 SDK는 사용하지 않습니다.소스가 아닌 i386 및 x86 64 CPU 아키텍처에서만 사용할 수 있으므로 Raspberry Pi에서 ARM CPU 아키텍처를 사용할 수 없습니다.
대신 PCSC lite을 사용하겠습니다.
systemd-devel
패키지와 Ubuntu용 libudev-dev
패키지가 필요합니다.# Fedora
sudo dnf install systemd-devel
# Ubuntu
sudo apt-get install libsystemd-dev libudev-dev
그런 다음 소스 코드를 다운로드, 압축 해제, 구성, 구축 및 설치할 수 있습니다.wget https://pcsclite.apdu.fr/files/pcsc-lite-1.9.3.tar.bz2
tar -xf pcsc-lite-1.9.3.tar.bz2
cd pcsc-lite-1.9.3/
./configure
make
sudo make install
3. 컴퓨터에서 ACR122U가 감지되었는지 확인(옵션)
응용 프로그램을 만들기 전에 컴퓨터에서 카드 리더기를 인식할 수 있는지 확인하는 것이 좋습니다. 카드 리더기를 삽입하고
pcsc-tools
패키지를 설치한 다음 pscs_scan
명령을 실행하십시오.카드 리더기가 감지되지 않으면 다음 메시지가 표시됩니다.
Waiting for the first reader...
솔루션은 pcscd
서비스를 다시 시작하는 것입니다.sudo systemctl restart pcscd
계속 작동하지 않으면 다음 명령을 실행할 수 있습니다.systemctl status pcscd
이것은 왜 카드 리더가 검출되지 않았는지 알려줄 수 있다.4. 응용 프로그램 만들기
이제 응용 프로그램을 만듭니다.나는 gcc 컴파일러에서 C 언어를 사용할 것이지만, 그것은 C++와 g++에서도 호환된다.독자와의 교류를 위해 우리는 PC/SC Lite API (WinSCard)을 사용할 것이다.모든 코드는
main.c
파일로 GitHub에서 얻을 수 있습니다.이 자습서에서는 MIFARE Ultralight 및 MIFARE Classic 1K 태그를 사용합니다.
먼저 WinS카드 라이브러리를 포함할 수 있는지 살펴보겠습니다.
#include <winscard.h>
int main() {
return 0;
}
WinS카드 라이브러리의 헤더 파일은 /usr/local/include/PCSC
에 있습니다.따라서 이 프로그램은 다음과 같은 방법으로 컴파일하고 실행할 수 있습니다.gcc main.c -lpcsclite -I/usr/local/include/PCSC
./a.out
현재 우리는 WinSCard API의 문서를 사용하여 독자와 통신할 것이다.첫 번째 단계는 컨텍스트를 설정하는 것입니다(응용 프로그램이 끝날 때 게시해야 함).#include <stdio.h>
#include <stdlib.h>
#include <winscard.h>
SCARDCONTEXT applicationContext;
void establishContext() {
SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &applicationContext);
}
void releaseContext() {
SCardReleaseContext(applicationContext);
}
int main() {
establishContext();
releaseContext();
return 0;
}
우리는 미래의 함수에 그것을 필요로 하기 때문에 전역 변수 applicationContext
을 사용합니다.이 코드는 작업이 성공했는지 확인하지 않습니다.그래서 우리는 다음과 같이 덧붙였다.#include <stdio.h>
#include <stdlib.h>
#include <winscard.h>
SCARDCONTEXT applicationContext;
void establishContext() {
LONG status = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &applicationContext);
if (status == SCARD_S_SUCCESS) {
printf("Context established\n");
} else {
printf("Establish context error: %s\n", pcsc_stringify_error(status));
exit(1);
}
}
void releaseContext() {
LONG status = SCardReleaseContext(applicationContext);
if (status == SCARD_S_SUCCESS) {
printf("Context released\n");
} else {
printf("Release context error: %s\n", pcsc_stringify_error(status));
exit(1);
}
}
int main() {
establishContext();
releaseContext();
return 0;
}
이것은 모든 조작의 성공에 대한 정보를 제공하고 고장이 나면 프로그램을 종료합니다.그런 다음 사용 가능한 독자를 나열해야 합니다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winscard.h>
SCARDCONTEXT applicationContext;
LPSTR reader = NULL;
void establishContext() {}
void releaseContext() {}
void listReaders() {
DWORD readers = SCARD_AUTOALLOCATE;
LONG status = SCardListReaders(applicationContext, NULL, (LPSTR)&reader, &readers);
if (status == SCARD_S_SUCCESS) {
char *p = reader;
while (*p) {
printf("Reader found: %s\n", p);
p += strlen(p) +1;
}
} else {
printf("List reader error: %s\n", pcsc_stringify_error(status));
exit(1);
}
}
void freeListReader() {
LONG status = SCardFreeMemory(applicationContext, reader);
if (status == SCARD_S_SUCCESS) {
printf("Reader list free\n");
} else {
printf("Free reader list error: %s\n", pcsc_stringify_error(status));
exit(1);
}
}
int main() {
establishContext();
listReaders();
freeListReader();
releaseContext();
return 0;
}
글로벌 변수 reader
을 추가했습니다. 이 변수는 카드 리더기를 식별하여 사용할 수 있도록 합니다.함수 SCardListReaders
은 reader
에 메모리를 분배하고 프로그램이 끝나기 전에 SCardFreeMemory
함수로 메모리를 방출해야 한다.카드 리더기를 삽입하는 동안 오류가 발생하면 컴퓨터가 ACR122U를 감지했는지 확인하십시오.SCardListReaders
함수가 실패하면 프로그램은 상하문을 방출하지 않고 종료됩니다.내가 이런 행동을 유지하는 것은 이 강좌를 가능한 한 간단하게 하기 위해서이지만, 이것은 네가 실제 응용 프로그램에서 하고 싶은 일이 아니다.다음 단계는 탭과 연결합니다.카드 리더기에 이름표(예: MIFARE Ultralight)를 배치하고 다음 코드를 실행합니다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winscard.h>
SCARDCONTEXT applicationContext;
LPSTR reader = NULL;
SCARDHANDLE connectionHandler;
DWORD activeProtocol;
void establishContext() {}
void releaseContext() {}
void listReaders() {}
void freeListReader() {}
void connectToCard() {
activeProtocol = -1;
LONG status = SCardConnect(applicationContext, reader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &connectionHandler, &activeProtocol);
if (status == SCARD_S_SUCCESS) {
printf("Connected to card\n");
} else {
printf("Card connection error: %s\n", pcsc_stringify_error(status));
exit(1);
}
}
void disconnectFromCard() {
LONG status = SCardDisconnect(connectionHandler, SCARD_LEAVE_CARD);
if (status == SCARD_S_SUCCESS) {
printf("Disconnected from card\n");
} else {
printf("Card deconnection error: %s\n", pcsc_stringify_error(status));
exit(1);
}
}
int main() {
establishContext();
listReaders();
connectToCard();
disconnectFromCard();
freeListReader();
releaseContext();
return 0;
}
여기에는 두 개의 글로벌 변수가 있습니다.connectionHandler
: 라벨과 통신하는 데 사용됩니다.activeProtocol
: 이것은 이 연결의 이미 정해진 협의입니다.WinS카드가 사용할 프로토콜을 선택하도록 하기 때문에 추적 프로토콜이 필요합니다. 왜냐하면 우리는 SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1
을 SCardConnect
함수의 네 번째 매개 변수로 사용하기 때문입니다.예를 들어 SCARD_PROTOCOL_T0
만 제공하여 사용할 프로토콜을 지정할 수 있습니다.탭에 사용자 정의 명령을 보내려면 프로토콜이 필요합니다.현재 우리는 탭과 연결되어 있으며, 예를 들어 탭의 ATR을 얻을 수 있다.ATR은 카드의 유형을 식별하는 데 사용할 수 있습니다.전체 목록은 here에서 얻을 수 있습니다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winscard.h>
SCARDCONTEXT applicationContext;
LPSTR reader = NULL;
SCARDHANDLE connectionHandler;
DWORD activeProtocol;
void establishContext() {}
void releaseContext() {}
void listReaders() {}
void freeListReader() {}
void connectToCard() {}
void disconnectFromCard() {}
void getCardInformation() {
BYTE ATR[MAX_ATR_SIZE] = "";
DWORD ATRLength = sizeof(ATR);
char readerName[MAX_READERNAME] = "";
DWORD readerLength = sizeof(readerName);
DWORD readerState;
DWORD readerProtocol;
LONG status = SCardStatus(connectionHandler, readerName, &readerLength, &readerState, &readerProtocol, ATR, &ATRLength);
if (status == SCARD_S_SUCCESS) {
printf("\n");
printf("Name of the reader: %s\n", readerName);
printf("ATR: ");
for (int i=0; i<ATRLength; i++) {
printf("%02X ", ATR[i]);
}
printf("\n\n");
} else {
printf("Get card information error: %s\n", pcsc_stringify_error(status));
exit(1);
}
}
int main() {
establishContext();
listReaders();
connectToCard();
getCardInformation();
disconnectFromCard();
freeListReader();
releaseContext();
return 0;
}
마지막으로, 우리는 리더에게 사용자 정의 명령을 보낼 수 있다.ACR122U webpage으로 돌아가면 API Driver Manual of ACR122U NFC Contactless Smart Card Reader을 다운로드할 수 있습니다.이 문서는 우리가 독자에게 보낼 수 있는 명령 목록을 보여 줍니다.예를 들어, 카드 리더기의 펌웨어 버전(24페이지)을 읽어들입니다.관련 명령은 FFh 00h 48h 00h 00h
이다.#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <winscard.h>
SCARDCONTEXT applicationContext;
LPSTR reader = NULL;
SCARDHANDLE connectionHandler;
DWORD activeProtocol;
void establishContext() {}
void releaseContext() {}
void listReaders() {}
void freeListReader() {}
void connectToCard() {}
void getCardInformation() {}
void sendCommand(uint8_t command[], unsigned short commandLength) {
const SCARD_IO_REQUEST *pioSendPci;
SCARD_IO_REQUEST pioRecvPci;
uint8_t response[300];
unsigned long responseLength = sizeof(response);
switch(activeProtocol) {
case SCARD_PROTOCOL_T0:
pioSendPci = SCARD_PCI_T0;
break;
case SCARD_PROTOCOL_T1:
pioSendPci = SCARD_PCI_T1;
break;
default:
printf("Protocol not found\n");
exit(1);
}
LONG status = SCardTransmit(connectionHandler, pioSendPci, command, commandLength, &pioRecvPci, response, &responseLength);
if (status == SCARD_S_SUCCESS) {
printf("Command sent: \n");
for (int i=0; i<commandLength; i++) {
printf("%02X ", command[i]);
}
printf("\nResponse: \n");
for (int i=0; i<responseLength; i++) {
printf("%02X ", response[i]);
}
printf("\n\n");
} else {
printf("Send command error: %s\n", pcsc_stringify_error(status));
exit(1);
}
}
int main() {
establishContext();
listReaders();
connectToCard();
getCardInformation();
printf("Firmware command:\n");
uint8_t firmwareCommand[] = { 0xFF, 0x00, 0x48, 0x00, 0x00 };
unsigned short firmwareCommandLength = sizeof(firmwareCommand);
sendCommand(firmwareCommand, firmwareCommandLength);
disconnectFromCard();
freeListReader();
releaseContext();
return 0;
}
연결된 프로토콜이 실행될 때 결정되기 때문에 switch
문구가 필요합니다.만약 우리가 SCARD_PROTOCOL_T0
함수에서 SCardConnect
만 사용한다면, 우리는 switch 문장이 아닌 pioSendPci = SCARD_PCI_T0;
을 직접 작성할 수 있다.리더와 탭에 사용자 정의 명령을 보낼 수 있는 프로그램이 실행 중입니다.MIFARE Ultralight 및 MIFARE Classic 1k에서 데이터를 전송하고 읽어들이는 데 사용합니다.MIFARE Ultralight부터 시작하겠습니다.
MIFARE Ultralight는 4바이트씩 16페이지로 구성됩니다.사용자 메모리는 5페이지부터 보호되지 않습니다. (인증이 없는 상황에서 접근할 수 있습니다.)자세한 내용은 태그의 data sheet에서 확인할 수 있습니다.
API Driver Manual of ACR122U의 16페이지와 17페이지에서 읽기와 쓰기 명령을 볼 수 있습니다.5페이지(
04h
페이지)에서 16바이트를 읽고 5페이지에 4바이트를 쓰는 함수를 작성합니다.#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <winscard.h>
SCARDCONTEXT applicationContext;
LPSTR reader = NULL;
SCARDHANDLE connectionHandler;
DWORD activeProtocol;
void establishContext() {}
void releaseContext() {}
void listReaders() {}
void freeListReader() {}
void connectToCard() {}
void disconnectFromCard() {}
void getCardInformation() {}
void sendCommand(uint8_t command[], unsigned short commandLength) {}
void mifareUltralight() {
printf("### MIFARE Ultralight ###\n");
uint8_t pageNumber = 0x04;
// Read 4 blocks (16 bytes) starting from pageNumber
uint8_t readCommand[] = { 0xFF, 0xB0, 0x00, pageNumber, 0x10 };
unsigned short readCommandLength = sizeof(readCommand);
sendCommand(readCommand, readCommandLength);
// Write 1 block (4 bytes) to pageNumber
uint8_t data[] = { 0x00, 0x01, 0x02, 0x03 };
uint8_t writeCommand[9] = { 0xFF, 0xD6, 0x00, pageNumber, 0x04 };
unsigned short writeCommandLength = sizeof(writeCommand);
for (int i=0; i<4; i++) {
writeCommand[i+5] = data[i];
}
sendCommand(writeCommand, writeCommandLength);
}
int main() {
establishContext();
listReaders();
connectToCard();
getCardInformation();
printf("Firmware command:\n");
uint8_t firmwareCommand[] = { 0xFF, 0x00, 0x48, 0x00, 0x00 };
unsigned short firmwareCommandLength = sizeof(firmwareCommand);
sendCommand(firmwareCommand, firmwareCommandLength);
mifareUltralight();
disconnectFromCard();
freeListReader();
releaseContext();
return 0;
}
마지막으로 MIFARE Classic 1k에 대해 동일한 작업을 수행합니다.레이블은 4개의 블록에 16개의 섹터로 구성되며 각 블록은 16바이트로 구성됩니다.데이터는 6바이트 키로 보호되며 기본 키는 FFh FFh FFh FFh FFh FFh
입니다.자세한 내용은 태그의 data sheet에서 확인할 수 있습니다.MIFARE Classic 1k를 사용하려면 API Driver Manual of ACR122U의 12페이지와 13페이지와 같이 섹터에 액세스하기 전에 인증해야 합니다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <winscard.h>
SCARDCONTEXT applicationContext;
LPSTR reader = NULL;
SCARDHANDLE connectionHandler;
DWORD activeProtocol;
void establishContext() {}
void releaseContext() {}
void listReaders() {}
void freeListReader() {}
void connectToCard() {}
void disconnectFromCard() {}
void getCardInformation() {}
void sendCommand(uint8_t command[], unsigned short commandLength) {}
void mifareUltralight() {}
void mifareClassic() {
printf("### MIFARE Classic ###\n");
uint8_t blockNumber = 0x04;
// Load Authentication Keys
uint8_t key[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
uint8_t authenticationKeysCommand[11] = { 0xFF, 0x82, 0x00, 0x00, 0x06 };
unsigned short authenticationKeysCommandLength = sizeof(authenticationKeysCommand);
for (int i=0; i<6; i++) {
authenticationKeysCommand[i+5] = key[i];
}
sendCommand(authenticationKeysCommand, authenticationKeysCommandLength);
// Authenticate
uint8_t authenticateCommand[] = { 0xFF, 0x86, 0x00, 0x00, 0x05, 0x01, 0x00, blockNumber, 0x60, 0x00 };
unsigned short authenticateCommandLength = sizeof(authenticateCommand);
sendCommand(authenticateCommand, authenticateCommandLength);
// Read 1 block (16 bytes) at blockNumber
uint8_t readCommand[] = { 0xFF, 0xB0, 0x00, blockNumber, 0x10 };
unsigned short readCommandLength = sizeof(readCommand);
sendCommand(readCommand, readCommandLength);
// Write 1 block (16 bytes) at blockNumber
uint8_t data[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
uint8_t writeCommand[21] = { 0xFF, 0xD6, 0x00, blockNumber, 0x10 };
unsigned short writeCommandLength = sizeof(writeCommand);
for (int i=0; i<16; i++) {
writeCommand[i+5] = data[i];
}
sendCommand(writeCommand, writeCommandLength);
}
int main() {
establishContext();
listReaders();
connectToCard();
getCardInformation();
printf("Firmware command:\n");
uint8_t firmwareCommand[] = { 0xFF, 0x00, 0x48, 0x00, 0x00 };
unsigned short firmwareCommandLength = sizeof(firmwareCommand);
sendCommand(firmwareCommand, firmwareCommandLength);
//mifareUltralight();
mifareClassic();
disconnectFromCard();
freeListReader();
releaseContext();
return 0;
}
5. 결론
이제 ACR122U를 사용하여 MIFARE Ultralight 및 Classic 1k에서 데이터를 전송하고 검색하는 방법을 알 수 있습니다.대부분의 응용 프로그램에 있어서 이 정도면 충분할 것이다.그렇지 않으면 다음을 볼 수 있습니다.
Reference
이 문제에 관하여(ACR122U(Linux)를 사용하여 C/C++ 응용 프로그램 만들기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/rylern/create-a-c-c-app-with-the-acr122u-linux-5bg3텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)