Search code examples
c#wpfformatnfcndef

How to read and write to NFC tags in NDEF format using C# compatible with Android?


I am working with Mifare Ultralight C tags and write data in NDEF format to them. Am using NDEF-NFC library to create raw bytes for my NDEF message. My NDEF message is

var spRecord = new NdefTextRecord {
        Text = "1",
        LanguageCode = "en"
};
var msg = new NdefMessage { spRecord };

The output I get is D1 01 04 54 02 65 6E 31 (hexadecimal). If I write this byte array to the tag as-is:

  • After reading from the tag and using the same NDEF-NFC library I am able to convert back to the desired NDEF message.
  • Android applications do not recognize the NDEF message. I tried NFC Tools for Android.

Following the suggestions in Writing Ndef to NFC tag in Windows Form Application C# using ACR122U as Reader/Writer, I modified my byte array to write 03 08 D1 01 04 54 02 65 6E 31 FE 00 instead of the original byte array produced by the library.

  • Now I'm able to read the NDEF message using Android apps.

  • But on trying to convert the byte array to an NDEF message object using the library, I get the following error:

    Ndef parse error: Expected Message Begin missing in first record.

How can I read back the NDEF message correctly?


Solution

  • As you already found, NFC Forum Type 2 tags (such as MIFARE Ultralight, NTAG, etc.) require an NDEF message to be embedded into a NDEF TLV (tag-length-value) structure. This means that you prepend the tag 03 and the length of the NDEF message to the message (value) itself. Thus, you get

    +-----+--------+-------------------------+
    | TAG | LENGTH | VALUE                   |
    | 03  | 08     | D1 01 04 54 02 65 6E 31 |
    +-----+--------+-------------------------+
    

    In addition you may add a Terminator TLV (tag = FE, length = 00) to indicate that the remaining data area on the tag can be skipped from processing.

    The NDEF library that you use only processes NDEF messages and not the container format that is needed for storing the data on an NFC tag. Thus, you need to process that part yourself.

    Pack NDEF message into TLV structure

    var msg = new NdefMessage { ... };
    var msgBytes = msg.toByteArray();
    var ndefTlvLen = new byte[(msgBytes.Length < 255) ? 1 : 3];
    if (msgBytes.Length < 255) {
        ndefTlvLen[0] = (byte)(msgBytes.Length);
    } else {
        ndefTlvLen[0] = (byte)0x0FF;
        ndefTlvLen[1] = (byte)((msgBytes.Length >> 8) & 0x0FF);
        ndefTlvLen[2] = (byte)(msgBytes.Length & 0x0FF);
    }
    var tagData = new byte[1 + ndefTlvLen.Length + msgBytes.Length + 2];
    int offset = 0;
    tagData[offset++] = (byte)0x003;
    Array.Copy(ndefTlvLen, 0, tagData, offset, ndefTlvLen.Length);
    offset += ndefTlvLen.Length;
    Array.Copy(msgBytes, 0, tagData, offset, msgBytes.Length);
    offset += msgBytes.Length;
    tagData[offset++] = (byte)0x0FE;
    tagData[offset++] = (byte)0x000;
    

    Unpack NDEF message from TLV structure

    var tagData = ...; // byte[]
    var msg;
    int offset = 0;
    while (offset < tagData.Length) {
        byte tag = tagData[offset++];
        int len = (tagData[offset++] & 0x0FF);
        if (len == 255) {
            len = ((tagData[offset++] & 0x0FF) << 8);
            len |= (tagData[offset++] & 0x0FF);
        }
        if (tag == (byte)0x03) {
            var msgBytes = new byte[len];
            Array.Copy(tagData, offset, msgBytes, 0, len);
            msg = NdefMessage.FromByteArray(msgBytes);
        } else if (tag == (byte)0xFE) {
            break;
        }
        offset += len;
    }