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 主要用在描述現在想嘗試找什麼規格的 tag
struct 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 device
nm 是 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 target
pnmModulations 是 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?
回覆刪除