How to communicate with an NFC reader |
------------------------------------- |
|
NOTE: this is low level documentation if you want to understand how |
to communicate with the PN532 in the ACS ACR122, it is not necessary to |
understand this to use the library |
|
To use a device with a PN532 in NFCIP mode (P2P) is rather straightforward as |
is demonstrated by this library. This document will show you what APDUs to send |
at which moment as briefly as possible. See [1,2,3,7] for more detailed |
information on how everything is supposed to fit together. This document |
describes the required commands for the PN532 to communicate in P2P mode. The |
hardware I tested this with is the ACS ACR122 [4] with the NXP PN532 chip. |
This is the (relatively cheap) reader sold by for example TikiTag [5]. |
|
There are two modes considered here. INITIATOR and TARGET. It is a matter of |
sending the right APDUs to the reader. In Java 6 you can use the |
"javax.smartcardio.*" API for this [6]. For other platforms there are probably |
similar ways of communicating with readers. |
|
Communicating with the reader happens through APDUs (which are byte arrays of |
data) in which certain functionality of for example a smart card are accessed. |
The ACS ACR122 is however a little bit different as it also supports "pseudo |
APDUs" which talk to the reader itself rather then to a smart card. By sending |
a specific (pseudo) APDU you can access the functionality of the PN532 chip |
built in, or some reader specific functionality like modifying the status of |
the LED. |
|
The header for sending commands meant for the PN532 is: |
|
0xff 0x00 0x00 0x00 0xii |
|
Where "ii" is the size of the rest of the command including the command |
instruction. An example of an actual APDU command sent to the terminal to send |
data to a target (see below) looks like this (>>). The response is |
also shown (<<) |
|
>> 0xff 0x00 0x00 0x00 0x09 0xd4 0x40 0x01 0x30 0x31 0x32 0x33 0x34 0x00 |
<< 0xd5 0x41 0x00 0x30 0x31 0x32 0x33 0x34 0x00 0x90 0x00 |
|
This sends the data `0x30 0x31 0x32 0x33 0x34 0x00` which is a string |
representation of `01234`. The response includes the same data in this example. |
|
We also didn't mention that the responses to the commands all end with 0x90 |
0x00 as that is always the case with successfully executed APDUs. In case of a |
wrong command the result will only be two bytes which indicate an error. See |
for example [2] for a description of some errors for the ACR122. |
|
NFCIP Initiator Mode |
-------------------- |
The first step is to set the mode to initiator and wait for a target to appear. |
This is done by sending the command "InJumpForDEP" which is "0xd4 0x56". It has |
a few parameters that are encoded in the APDU. A full example: |
|
Configure as initiator and wait for targets |
------------------------------------------- |
There are two ways to do this. You can ask for a target and the first one to |
reply becomes your target, or the other method is to ask for multiple targets |
and select the one you want (possibly alternating), so you can manage two |
targets at the same time. |
|
Activating one target |
--------------------- |
This is used to initialize and activate a target directly with one command, you |
get one active target this way. |
|
** Command to PN532 ** |
0xd4 0x56 InJumpForDEP instruction code |
0x00 Look for Passive/Active Target |
(0x00 = passive, 0x01 = active) |
0x02 Baud Rate |
(0x00 = 106kbps, 0x01 = 212kbps, 0x02 = 424kbps) |
0x01 Whether or not there is a payload in this command |
(0x01 = yes) |
0x00 0xff 0xff 0x00 0x00 Polling Request? |
|
Every command is answered with a response, in this case as soon as a target is |
in range. |
|
** Response from PN532 ** |
0xd5 0x57 InJumpForDEP response code |
0x00 Status |
(0x00 = no error) |
0x01 Target number (used for sending data later) |
|
This is followed by the ATR_RES bytes which we won't be using in the rest of |
the protocol. |
|
Initializing and activate one or more targets |
--------------------------------------------- |
There is also another way to select and initialize a target, or multiple |
targets. The PN532 supports two targets at a time. This command is to list and |
initialize one or more targets: |
|
** Command to PN532 ** |
0xd4 0x4a InListPassivTargets instruction code |
0x01 Look for this number of targets |
0x02 Baud Rate |
(0x00 = 106kbps, 0x01 = 212kbps, 0x02 = 424kbps) |
0x00 0xff 0xff 0x00 0x00 Polling Request? |
|
** Response from PN532 ** |
0xd5 0x4b InListPassivTargets response code |
0x01 Number of targets found |
(here 1 target found) |
0x01 The following bytes describe target 1 |
0x12 The target information has 12 bytes including |
this one |
0x01 Maybe the real communication speed? |
0x01 0xFE 0xDD 0x8E 0xCF 0x70 0x29 0xE2 |
This target NFCID3 |
(used for selecting it with InSelect later) |
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 |
Padding? |
|
Activate target: |
|
** Command to PN532 ** |
0xd4 0x50 InATR instruction code |
0x01 Activate target with this number |
(here number 1) |
0x01 0xFE 0xDD 0x8E 0xCF 0x70 0x29 0xE2 |
The NFCID3 from previous response |
|
** Response from PN532 ** |
0xd5 0x51 InATR` response code |
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 |
0x00 0x00 0x00 0x00 0x00 0x00 0x0A 0x00 |
ATR_RES? |
|
The sending and receiving of data is the same as with the other method (see |
below). The advantage here is that you can scan for targets and select the one |
you need, allowing for multiple targets at the same time! |
|
Send data to a target |
--------------------- |
|
** Command to PN532 ** |
0xd4 0x40 InDataExchange instruction code |
0x01 Target to send to |
(see response from previous command) |
... ... ... The rest are the bytes to be send to the target. |
The maximum number of bytes seems to be 252? |
|
** Response from PN532 ** |
0xd5 0x41 InDataExchange reponse code |
0x00 Status |
(0x00 = no error) |
... ... ... The rest are the bytes received from the target, |
this is how the target sends data to the |
initiator by replying with it in the response |
to the received data |
|
Release a target |
---------------- |
|
** Command to PN532 ** |
0xd4 0x52 InRelease instruction code |
0x01 Target to release |
(0x00 = release all targets) |
|
** Response from PN532 ** |
0xd5 0x53 InRelease response code |
0x00 Status |
(0x00 = no error) |
|
NFCIP Target Mode |
----------------- |
|
Configure as target and wait for initiators |
------------------------------------------- |
|
** Command to PN532 ** |
0xd4 0x8c TgInitAsTarget instruction code |
0x00 Acceptable modes |
(0x00 = allow all, 0x01 = only allow to be |
initialized as passive, 0x02 = allow DEP only) |
|
_6 bytes (_MIFARE_)_: |
0x08 0x00 SENS_RES |
0x12 0x34 0x56 NFCID1 |
0x40 SEL_RES |
|
_18 bytes (_Felica_)_: |
0x01 0xfe 0xa2 0xa3 0xa4 0xa5 0xa6 0xa7 |
NFCID2 |
0xc0 0xc1 0xc2 0xc3 0xc4 0xc5 0xc6 0xc7 |
? |
0xff 0xff System parameters? |
0xaa 0x99 0x88 0x77 0x66 0x55 0x44 0x33 0x22 0x11 |
NFCID3 |
0x00 ? |
0x00 ? |
|
This is the response when an initiator activated this target: |
|
** Response from PN532 ** |
0xd5 0x8d TgInitAsTarget response code |
0x04 Mode |
(0x04 = DEP, 106kbps) |
... ... ... ? |
|
Receive data from initiator |
--------------------------- |
|
** Command to PN532 ** |
0xd4 0x86 TgGetData instruction code |
|
** Response from PN532 ** |
0xd5 0x87 TgGetData response code |
0x00 Status |
(0x00 = no error, bit 6 set: See "Meta Chaining") |
... ... ... The rest are the bytes received from initiator |
|
Send data to initiator |
---------------------- |
|
** Command to PN532 ** |
0xd4 0x8e TgSetData instruction code |
... ... ... The rest are the bytes to be send to initiator |
|
** Reponse from the PN532 ** |
0xd5 0x8f TgSetData response code |
0x00 Status |
(0x00 = no error) |
|
Send data to initiator (Meta Chaining) |
-------------------------------------- |
|
** Command to PN532 ** |
0xd4 0x94 TgSetMetaData instruction code |
... ... ... The rest are the bytes to be send to initiator |
|
** Reponse from the PN532 ** |
0xd5 0x95 TgSetMetaData response code |
0x00 Status |
(0x00 = no error) |
|
Chaining |
-------- |
Whenever you want to send more data then possible (the amount of data is |
seemingly limited to 252 bytes) you have can use "chaining". There are two ways |
to do this with the PN532. You can either implement it yourself completely or |
use the "meta chaining" provided by the PN532. Below both techniques will be |
shown: |
|
Initiator Meta Chaining |
----------------------- |
First we look at the situation from the initiator. Using meta chaining the |
initiator uses the InDataExchange command specifying the target and setting bit |
6 of the target field to indicate that there is more data coming. This bit 6 |
remains set while there is more data and is removed in the last data block. |
|
The response from the InDataExchange command can be ignored, except with the |
last block where bit 6 is not set. At this point the returned data is the data |
the target wants to send back. The status byte of the returned data indicates |
whether also the target wants to send more data back than just one block. If |
bit 6 is set in the status field the initiator can request the rest using an |
empty InDataExchange command, just specifying the target. |
|
|
InDataExchange(target byte with bit 6 set, block 1) |
InDataExchange(target byte with bit 6 set, block 2) |
: : |
InDataExchange(target byte with bit 6 cleared, block n) |
|
look at the response code from the last InDataExchange, if bit 6 is set |
there is more data coming than just the data in this block |
|
InDataExchange(target), while status byte has bit 6 set |
|
Target Meta Chaining |
-------------------- |
The target looks at the status field of TgGetData to see whether or not more |
data is coming in this transfer. While this bit is set the target can request |
more data with TgGetData. |
|
Once all data has been received the target can send back data. No bit needs to |
be set when more data is being sent. Just a different command. For a single |
block one uses TgSetData and for sending multiple blocks one uses TgSetMetaData. |
|
*FIXME* What about the last block? This is done using TgSetData again? |
|
Custom Chaining |
--------------- |
When using custom chaining, so without setting or reading bit 6 or the target |
and status fields the communication is a little bit different for the target. |
Behavior for the initiator stays the same (except that bit 6 is not set |
anymore). The target now can't send TgGetData repeatedly, but has to use |
TgSetData without sending any actual data before using TgGetData again. In this |
situation TgSetMetaData is not used. |
|
References |
---------- |
[1] http://www.diganttechnologies.com/nfc_1.pdf |
(retrieved November 15th 2008) |
[2] http://www.acs.com.hk/download/ACR122/API_ACR122U.pdf |
(retrieved November 15th 2008) |
[3] http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-340.pdf |
(retrieved November 15th 2008) |
[4] http://www.acs.com.hk/acr122.php |
(retrieved November 15th 2008) |
[5] http://www.tikitag.com/ |
(retrieved November 15th 2008) |
[6] http://java.sun.com/javase/6/docs/jre/api/security/smartcardio/spec/ |
(retrieved November 15th 2008) |
[7] _Beyond Device Pairing: New Interactions on NFC Enabled Mobile Phones_ |
http://www.cs.washington.edu/homes/yanokwa/papers/anokwa_qualspaper.pdf |
(retrieved November 17th 2008) |