2014/10/06

libnfc example

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);
}






3 則留言:

  1. 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?

    回覆刪除
  2. 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.

    回覆刪除
  3. 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?

    回覆刪除