Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android nfcA.connect(), nfcA.transceive(), nfcA.setTimeout() and nfcA.getMaxTransceiveLength()

I have a a number of newbie NfcA questions. There seems to be little guidance on this in the docs and elsewhere on the web, so I hope no-one minds me stringing a few basic questions together here...

I am using nfcA.transceive() to write data to my NTAG213 tag like this:

    byte[] result = nfcA.transceive(new byte[] {
            (byte)0xA2,  // WRITE
            (byte)(pageNum & 0x0ff),
            myData[0], myData[1], myData[2], myData[3]
    });

1. The result array is a single byte of value 10. What does this mean and what other values should I look out for?

I am also using the same method to read data from my NTAG213 tags:

    byte[] result = nfcA.transceive(new byte[] {
            (byte)0x30,  // READ
            (byte)(pageNum & 0x0ff)
    });

2. I expected this to return 4 bytes of user data (i.e., the 4 bytes that correspond to my pageNum), but it returned 16 bytes. Why is that the case?

3. Is it good practise to check nfcA.isConnected() before calling nfcA.connect() and, if so, is there likely to be any sigificant performance penalty in doing so? (I ask as I have seen code examples from reputable sources of both.)

4. Is it better to call nfcA.setTimeout() before or after nfcA.connect()?

5. For my NTAG213 tags nfcA.getMaxTransceiveLength() returns 253. Does that really mean I can write up to 251 bytes of user data (plus the 2 other bytes) in one go and, if so, is that advisable or is it better to write each page (4 bytes) with separate nfcA.transceive() calls?

like image 639
ban-geoengineering Avatar asked Oct 27 '16 15:10

ban-geoengineering


1 Answers

1. The result array for a WRITE command is a single byte of value 10. What does this mean and what other values should I look out for?

The value 10 (Ah in hexadecimal or 1010b in binary representation) is an explicit ACK, an acknowledgement returned when a command that returns no data succeeds.

The possible values are actual data, ACK, passive ACK, or NACK. These are defined by the NFC Forum Digital Protocol specification and by the NFC Forum Type 2 Tag Operation specification.

  1. If the command is expected to return actual data on success, the data is returned instead of an explicit ACK value.
  2. ACK is defined as a 4-bit short frame (see NFC Forum Digital Protocol specification and ISO/IEC 14443-3 for further details) with the value 1010b (Ah).
  3. A passive ACK is defined as the tag not sending a response at all within a certain timeout.
  4. NACK is defined as a 4-bit short frame with the value 0x0xb (where x is either 0 or 1).

The NTAG213/215/216 product data sheet is a bit more specific on possible NACK values:

  1. 0000b (0h) indicates an invalid command argument.
  2. 0001b (1h) indicates a parity or CRC error.
  3. 0100b (4h) indicates an invalid authentication counter overflow.
  4. 0101b (5h) indicates an EEPROM write error.

In addition to the above, the NFC stack implementations on some devices do not properly propagate NACK responses to the app. Instead they either throw a TagLostException or return null. Similarly, you might(?) get a TagLostException indicating a passive ACK.

Thus, you would typically check the result of the transceive method for the following (unless you send a command that is expected to result in a passive ACK):

try {
   response = nfca.transceive(command);
   if (response == null) {
       // either communication to the tag was lost or a NACK was received
   } else if ((response.length == 1) && ((response[0] & 0x00A) != 0x00A)) {
       // NACK response according to Digital Protocol/T2TOP
   } else {
       // success: response contains ACK or actual data
   }
} catch (TagLostException e) {
   // either communication to the tag was lost or a NACK was received
}

2. I expected the READ method to to return 4 bytes of user data (i.e. the 4 bytes that correspond to my pageNum), but it returned 16 bytes. Why is that the case?

The READ command is defined to return 4 blocks of data starting with the specified block number (in the NFC Forum Type 2 Tag Operation specification). Thus, if you send a READ command for block 4, you get the data of blocks 4, 5, 6, and 7.

3. Is it good practise to check nfcA.isConnected() before calling nfcA.connect() and, if so, is there likely to be any sigificant performance penalty in doing so?

If you receive the Tag handle directly from the NFC system service (through an NFC intent) the tag won't be connected. So unless you use the Tag handle before calling nfca.connect(), I don't see why you would want to call nfca.isConnected() before. However, calling that method before connecting has barely any performance overhead since calling isConnected() on a closed tag technology object will be handled by the famework API without calling into the NFC system service. Hence, it's not much more overhead than a simple if over a boolean member variable of the NfcA object.

4. Is it better to call nfcA.setTimeout() before or after nfcA.connect()?

I'm not sure about that one. However, the transceive timeout is typically reset on disconnecting the tag technology.

5. For my NTAG213 tags nfcA.getMaxTransceiveLength() returns 253. Does that really mean I can write up to 251 bytes of user data (plus the 2 other bytes) in one go and, if so, is that advisable or is it better to write each page (4 bytes) with separate nfcA.transceive() calls?

No, you can only write one block at a time. This is limited by the WRITE command of the NTAG213, which only supports one block as data input.

However, a transceive buffer size of 253 allows you to use the FAST_READ command to read multiple blocks (up to 62, so up to 45 for the NTAG213) at a time:

int firstBlockNum = 0;
int lastBlockNum = 42;
byte[] result = nfcA.transceive(new byte[] {
        (byte)0x3A,  // FAST_READ
        (byte)(firstBlockNum & 0x0ff),
        (byte)(lastBlockNum & 0x0ff),
});
like image 149
Michael Roland Avatar answered Nov 15 '22 18:11

Michael Roland