1. Brief Introduction
在使用libnfc相關的code, 第一步一定要先呼叫 nfc_init(), 它會做初始設定和配置相關資源
void nfc_init(nfc_context **context)
接著呼叫 nfc_open(), 它會找第一個可用的 nfc device
nfc_device* nfc_open(nfc_context *context, const nfc_connstring connstring)
參數 context 就是在 nfc_init() 用到的那個
參數 connstring 表示你想找有特定名字的device, 像"usb"之類的, 如果沒有特定就帶NULL
它會依照下列順序找:
(1) 環境變數裡預設值
(2) libnfc.conf 設定的值
(3) /etc/nfc/devices.d 裡找到
(4) auto-detected 第一個找到的值
找到之後, 它會宣告目前它在使用這個device, 並回傳device pointer
然後記得對這個 device 做初始化
int nfc_initiator_init(nfc_device *pnd)
struct nfc_modulation 表示不同ISO規格的資料
typedef struct {
nfc_modulation_type nmt;
nfc_baud_rate nbr;
} nfc_modulation;
其中 nfc_modulation_type為ISO標準typedef enum {
NMT_ISO14443A = 1,
NMT_JEWEL,
NMT_ISO14443B,
NMT_ISO14443BI, // pre-ISO14443B aka ISO/IEC 14443 B' or Type B'
NMT_ISO14443B2SR, // ISO14443-2B ST SRx
NMT_ISO14443B2CT, // ISO14443-2B ASK CTx
NMT_FELICA,
NMT_DEP,
} nfc_modulation_type;
然後 nfc_baud_rate 為 tag 的 baud rate, (每種規格有不同的baud rate)typedef enum {
NBR_UNDEFINED = 0,
NBR_106,
NBR_212,
NBR_424,
NBR_847,
} nfc_baud_rate;
nfc_modulation 主要用在描述現在想嘗試找什麼規格的 tagstruct nfc_target 則是用來儲存找到的 tag
typedef struct {
nfc_target_info nti;
nfc_modulation nm;
} nfc_target;
其中 nfc_modulation 只是紀錄它的ISO規格union nfc_target_info 則是儲存找到的這張 tag 的內容
typedef union {
nfc_iso14443a_info nai;
nfc_felica_info nfi;
nfc_iso14443b_info nbi;
nfc_iso14443bi_info nii;
nfc_iso14443b2sr_info nsi;
nfc_iso14443b2ct_info nci;
nfc_jewel_info nji;
nfc_dep_info ndi;
} nfc_target_info;
不同的 ISO 規格, 它能提供的資訊也不同我們有nfc device, nfc modulation, 就可以找 nfc target, 用底下這個function
int nfc_initiator_select_passive_target(nfc_device *pnd,
const nfc_modulation nm,
const uint8_t *pbtInitData,
const size_t szInitData,
nfc_target *pnt)
其中 pnd 是 nfc devicenm 是 nfc modulation
pbtInitData 可以帶 NULL, 有些 ISO 規格裡可以帶data給tag, 比如說ISO14443A裡, 可以帶 UID, 而 ISO14443B 可以帶 Application Family Identifier (AFI)
szInitData 則是前一項 pbtInitData的size
pnt 則是存找到的nfc target
最後記得要關device
void nfc_close(nfc_device *pnd)
以及要記得釋放nfc context資源
void nfc_exit(nfc_context *context)
2. Sample code
這份sample code從libnfc官方網頁上可以找到, 這是原始網頁
http://nfc-tools.org/index.php?title=Libnfc:quick_start_example
它可以找 ISO14443-A tag, (可能是最常見的tag)
#include <stdlib.h>
#include <nfc/nfc.h>
static void
print_hex(const uint8_t *pbtData, const size_t szBytes) {
size_t szPos;
for (szPos = 0; szPos < szBytes; szPos++) {
printf("%02x ", pbtData[szPos]);
}
printf("\n");
}
int
main(int argc, const char *argv[]) {
nfc_device *pnd;
nfc_target nt;
nfc_context *context;
nfc_init(&context);
if (context == NULL) {
printf("Unable to init libnfc (malloc)\n");
exit(EXIT_FAILURE);
}
pnd = nfc_open(context, NULL);
if (pnd == NULL) {
printf("ERROR: %s\n", "Unable to open NFC device.");
exit(EXIT_FAILURE);
}
if (nfc_initiator_init(pnd) < 0) {
nfc_perror(pnd, "nfc_initiator_init");
exit(EXIT_FAILURE);
}
printf("NFC reader: %s opened\n", nfc_device_get_name(pnd));
const nfc_modulation nmMifare = {
.nmt = NMT_ISO14443A,
.nbr = NBR_106,
};
if (nfc_initiator_select_passive_target(pnd, nmMifare, NULL, 0, &nt) > 0) {
printf("The following (NFC) ISO14443A tag was found:\n");
printf(" ATQA (SENS_RES): ");
print_hex(nt.nti.nai.abtAtqa, 2);
printf(" UID (NFCID%c): ", (nt.nti.nai.abtUid[0] == 0x08 ? '3' : '1'));
print_hex(nt.nti.nai.abtUid, nt.nti.nai.szUidLen);
printf(" SAK (SEL_RES): ");
print_hex(&nt.nti.nai.btSak, 1);
if (nt.nti.nai.szAtsLen) {
printf(" ATS (ATR): ");
print_hex(nt.nti.nai.abtAts, nt.nti.nai.szAtsLen);
}
}
nfc_close(pnd);
nfc_exit(context);
exit(EXIT_SUCCESS);
}
然後 compile它
$ gcc -o quick_start_example1 quick_start_example1.c -lnfc
執行結果
$ ./quick_start_example1
NFC reader: pn532_spi:/dev/spidev0.0 opened
The following (NFC) ISO14443A tag was found:
ATQA (SENS_RES): 00 04
UID (NFCID1): 45 d1 a9 51
SAK (SEL_RES): 08
3. Polling
前個例子是一執行馬上就掃當下讀的到的卡
也可以改成用 poll 的方式, 在一定的時間內讀到卡即可
int nfc_initiator_poll_target(nfc_device *pnd,
const nfc_modulation *pnmModulations,
const size_t szModulations,
const uint8_t uiPollNr,
const uint8_t uiPeriod,
nfc_target *pnt)
其中, pnd 是 nfc device, pnt 是 nfc targetpnmModulations 是 nfc_modulation array, 裡面放想掃的 modulation type
szModulations 是 pnmModulations 的 array 長度
uiPollNr 指要 poll 幾次, 可以 1~254 次, 如果是 0xFF 表示要永遠 poll
uiPeriod 表是每次 poll 間隔幾個單位, 1個單位內定是150ms, 2個單位就是300ms, 以此類推
回傳值是 poll 掃到的 nfc target 數目, <0 表示error, =0表示沒掃到
可以 poll 卡是不是接近之後, 也可以檢查卡是不是已經拿開
int nfc_initiator_target_is_present(nfc_device *pnd, const nfc_target *pnt)回傳 0 表示還在, 其它的值表示 error (即離開或讀不到了)
3.1 Polling sample code
相關的 sample code 可以參考 libnfc 裡的 nfc-poll.c
底下這個例子稍微簡化 nfc-poll.c,
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <nfc/nfc.h>
#include <nfc/nfc-types.h>
static nfc_device *pnd = NULL;
static nfc_context *context;
static void print_hex(const uint8_t *pbtData, const size_t szBytes) {
size_t szPos;
for (szPos = 0; szPos < szBytes; szPos++) {
printf("%02x ", pbtData[szPos]);
}
printf("\n");
}
static void stop_polling(int sig) {
if (pnd != NULL) {
nfc_abort_command(pnd);
} else {
nfc_exit(context);
exit(EXIT_FAILURE);
}
}
int main(int argc, const char *argv[]) {
signal(SIGINT, stop_polling);
const uint8_t uiPollNr = 20;
const uint8_t uiPeriod = 2;
const nfc_modulation nmModulations[1] = {
{ .nmt = NMT_ISO14443A, .nbr = NBR_106 },
};
const size_t szModulations = 1;
nfc_target nt;
int res = 0;
nfc_init(&context);
if (context == NULL) {
printf("Unable to init libnfc (malloc)\n");
exit(EXIT_FAILURE);
}
pnd = nfc_open(context, NULL);
if (pnd == NULL) {
printf("Unable to open NFC device\n");
exit(EXIT_FAILURE);
}
if (nfc_initiator_init(pnd) < 0) {
nfc_perror(pnd, "nfc_initiator_init");
nfc_close(pnd);
nfc_exit(context);
exit(EXIT_FAILURE);
}
printf("NFC reader: %s opened\n", nfc_device_get_name(pnd));
printf("NFC device will poll %d ms\n", uiPollNr * szModulations * uiPeriod * 150);
res = nfc_initiator_poll_target(pnd, nmModulations, szModulations, uiPollNr, uiPeriod, &nt);
if (res < 0) {
nfc_perror(pnd, "nfc_initiator_poll_target");
nfc_close(pnd);
nfc_exit(context);
exit(EXIT_FAILURE);
} else if (res == 0) {
printf("No target found\n");
} else {
printf("The following (NFC) ISO14443A tag was found:\n");
printf(" ATQA (SENS_RES): ");
print_hex(nt.nti.nai.abtAtqa, 2);
printf(" UID (NFCID%c): ", (nt.nti.nai.abtUid[0] == 0x08 ? '3' : '1'));
print_hex(nt.nti.nai.abtUid, nt.nti.nai.szUidLen);
printf(" SAK (SEL_RES): ");
print_hex(&nt.nti.nai.btSak, 1);
if (nt.nti.nai.szAtsLen) {
printf(" ATS (ATR): ");
print_hex(nt.nti.nai.abtAts, nt.nti.nai.szAtsLen);
}
}
printf("Waiting for card removing...\n");
while (0 == nfc_initiator_target_is_present(pnd, NULL)) {};
nfc_perror(pnd, "nfc_initiator_target_is_present");
printf("done\n");
nfc_close(pnd);
nfc_exit(context);
exit(EXIT_SUCCESS);
}
Hello. Greeting Mr. Wei-Chi Lai. I am new to NFC technology. I see you make a post about this libnfc example codes. I am currently working on a project using this libnfc. I have tried to study and searching to understand what is the meaning of this codes. I am trying to use a Raspberry Pi and NFC module PN532 to read and write an NFC tag. Can you give me explanation about this : nt.nti.nai.szAtsLen on print_hex(nt.nti.nai.abtAtqa, 2); I know it use function print_hex on the above.
回覆刪除print_hex(const uint8_t *pbtData, const size_t szBytes) {
size_t szPos;
for (szPos = 0; szPos < szBytes; szPos++) {
printf("%02x ", pbtData[szPos]);
}
printf("\n");
}
But how do we know the value for : const uint8_t *pbtData, const size_t szBytes
Where does nt.nti.nai.szAtsLen coming from?
Hi Josua,
回覆刪除nt is a type of struct nfc_target which is defined in nfc-types.h of libnfc. I believe the value of nt is filled within "nfc_initiator_poll_target(pnd, nmModulations, szModulations, uiPollNr, uiPeriod, &nt)" after a nfc target is detected. As you can see nt is passed a pointer in parameter in nfc_initiator_poll_target.
Hey. How can we read the data in the tag? These example is just reading the UID. And how can I do a write into the tag instead of just reading?
回覆刪除