为什么在使用 Android phone 读取 NFCtag 时得到的标签 ID 与使用专用 reader 读取时不同?

Why when reading NFCtag with Android phone you get different tag ID then when reading with dedicated reader?

我正在使用 Android Cilico F750,专用 RFID reader 是 CF-RS103。 RFID 标签类型为 MIFARE Ultralight C 型。

用专用卡读取时reader标签id为:2054270212(10位)。

但是当使用 Android phone 读取时,id 是:36139312876727556(17 位),反向 id 是:1316602805183616(16 位)。

有谁知道为什么会这样,是否可以将 10 位 ID 转换为 17 位 ID,反之亦然。

我使用 intents 来检测标签并解析 intent 我使用这个:

public void resolveIntent(Intent intent){

    String action = intent.getAction();

    if(NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)
            ||NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)
            ||NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action))

    {
        Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
        NdefMessage[] msgs;

        if(rawMsgs!=null)
        {
            msgs= new NdefMessage[rawMsgs.length];

            for(int i=0; i<rawMsgs.length; i++)
            {
                msgs[i]=(NdefMessage) rawMsgs[i];
            }
        }

        else
        {
            byte[] empty = new byte[0];
            byte[] id = intent.getByteArrayExtra(NfcAdapter.EXTRA_ID);
            Tag tag = (Tag) intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
            byte[] payload = dumpTagData(tag).getBytes();
            NdefRecord record = new NdefRecord(NdefRecord.TNF_UNKNOWN,empty,id,payload);
            NdefMessage msg = new NdefMessage(new NdefRecord[]{record});
            msgs= new NdefMessage[] {msg};
        }
        displayMsgs(msgs);
    }}

这是我的辅助函数:

private void displayMsgs(NdefMessage[] msgs)
{
    if(msgs==null || msgs.length==0) {
        return;
    }

    StringBuilder builder = new StringBuilder();
    List<ParsedNdefRecord> records= NdefMessageParser.parse(msgs[0]);
    final int size = records.size();

    for(int i=0;i<size;i++)
    {
        ParsedNdefRecord record = records.get(i);
        String str = record.str();
        builder.append(str).append("\n");
    }

    text.setText(builder.toString());
}



private String dumpTagData(Tag tag) {
StringBuilder sb = new StringBuilder();
byte[] id = tag.getId();
sb.append("ID (hex): ").append(toHex(id)).append('\n');
sb.append("ID (reversed hex):").append(toReversedHex(id)).append('\n');
sb.append("ID (dec): ").append(toDec(id)).append('\n');
sb.append("ID (reversed dec):").append(toReversedDec(id)).append('\n');

String prefix = "android.nfc.tech.";
sb.append("Technologies: ");
for (String tech: tag.getTechList()) {
    sb.append(tech.substring(prefix.length()));
    sb.append(", ");
}

sb.delete(sb.length() - 2, sb.length());

for (String tech: tag.getTechList()) {
    if (tech.equals(MifareClassic.class.getName())) {
        sb.append('\n');
        String type = "Unknown";

        try {
            MifareClassic mifareTag = MifareClassic.get(tag);

            switch (mifareTag.getType()) {
                case MifareClassic.TYPE_CLASSIC:
                    type = "Classic";
                    break;
                case MifareClassic.TYPE_PLUS:
                    type = "Plus";
                    break;
                case MifareClassic.TYPE_PRO:
                    type = "Pro";
                    break;
            }
            sb.append("Mifare Classic type: ");
            sb.append(type);
            sb.append('\n');

            sb.append("Mifare size: ");
            sb.append(mifareTag.getSize() + " bytes");
            sb.append('\n');

            sb.append("Mifare sectors: ");
            sb.append(mifareTag.getSectorCount());
            sb.append('\n');

            sb.append("Mifare blocks: ");
            sb.append(mifareTag.getBlockCount());
        } catch (Exception e) {
            sb.append("Mifare classic error: " + e.getMessage());
        }
    }

    if (tech.equals(MifareUltralight.class.getName())) {
        sb.append('\n');
        MifareUltralight mifareUlTag = MifareUltralight.get(tag);
        String type = "Unknown";
        switch (mifareUlTag.getType()) {
            case MifareUltralight.TYPE_ULTRALIGHT:
                type = "Ultralight";
                break;
            case MifareUltralight.TYPE_ULTRALIGHT_C:
                type = "Ultralight C";
                break;
        }
        sb.append("Mifare Ultralight type: ");
        sb.append(type);
    }
}

return sb.toString();
}

private String toHex(byte[] bytes) {
    StringBuilder sb = new StringBuilder();
    for (int i = bytes.length - 1; i >= 0; --i) {
        int b = bytes[i] & 0xff;
        if (b < 0x10)
            sb.append('0');
        sb.append(Integer.toHexString(b));
        if (i > 0) {
            sb.append(" ");
        }
    }
    return sb.toString();
}

private String toReversedHex(byte[] bytes) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < bytes.length; ++i) {
        if (i > 0) {
            sb.append(" ");
        }
        int b = bytes[i] & 0xff;
        if (b < 0x10)
            sb.append('0');
        sb.append(Integer.toHexString(b));
    }
    return sb.toString();
}

private long toDec(byte[] bytes) {
    long result = 0;
    long factor = 1;
    for (int i = 0; i < bytes.length; ++i) {
        long value = bytes[i] & 0xffl;
        result += value * factor;
        factor *= 256l;
    }
    return result;
}

private long toReversedDec(byte[] bytes) {
    long result = 0;
    long factor = 1;
    for (int i = bytes.length - 1; i >= 0; --i) {
        long value = bytes[i] & 0xffl;
        result += value * factor;
        factor *= 256l;
    }
    return result;
}`

编辑:我通过将 7 字节的十六进制 ID 截断为 4 字节设法解决了这个问题。 如果十进制 ID 的总长度小于 10 位,则使用此语句格式化十进制 ID,如果 DEC ID 小于 10 位,则基本上从左侧添加零:

String strFinal=String.format("%010d", Long.parseLong(str));

这篇描述如何将 ID 从 HEX8 转换为 DEC10 的文档也对我有很大帮助:https://www.batag.com/download/rfidreader/LF/RAD-A200-R00-125kHz.8H10D.EM.V1.1.pdf

非常感谢@Andrew 和@Karam 帮助我解决了这个问题!

我不知道你为什么总是想转换成十进制? 并请尝试详细解释您用于读取 UID 的代码。

关于您的号码并将 17 位数字转换为 10 位数字;我将它们都转换为十六进制:

36139312876727556(17digit) in Hex : 8064837A71AD04.

2054270212(10 digit) in Hex: 7A71AD04

正如您所注意到的,您只需对前三个字节进行 tirm 即可获得 10 位数字。

我相信他们都不是 UID。但是安德鲁说的 7 字节,你已经在你的照片中读到了它:(04:B5:71:7A:83:64:80)

所以我认为答案是,因为您正在将 7 字节 ID 转换为十进制,所以您会因为转换为十进制而获得可变长度的数字。

"The byte data type is an 8-bit signed two's complement integer. It has a minimum value of -128 and a maximum value of 127 (inclusive)."

来自https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html

可以生成具有 1,2 或 3 个字符的十进制数字,因此作为十进制,id 的长度可以变化。

看起来转换也出错了,因为理论上它也应该有负数。

如果您希望它是人类可读的,最好将其作为十六进制字符串处理。

Java中将ID的2页转换为十六进制的正确方法是

StringBuilder Uid;
for (int i = 0; i < result.length; i++) {
                        // byte 4 is a check byte
                        if (i == 3) continue;
                        Uid.append(String.format("%02X ", result[i]));
                    }

根据卡的规范 sheet 注意 https://www.nxp.com/docs/en/data-sheet/MF0ICU2_SDS.pdf(第 7.3.1 节)

有一个校验字节是 ID 的一部分,虽然它在同一张卡上总是相同的,并且仍然会给你一个唯一的 ID,但从技术上讲,它不是 ID 的一部分。

或者如果不是低水平阅读那么

https://developer.android.com/reference/android/nfc/Tag#getId()

将为您获取 ID。

请注意,“36139312876727556(17digit) and reversed id”转换为十六进制并反转实际时为7个字节,并以正确的数字开头。

10位正好是7字节数的前4字节也倒过来了

PC上的卡reader配置错误,当卡有7字节ID时默认配置显示ID为10位十进制数(4字节)

因此它必须丢失一些数据,它通过将 ID 截断到 7 字节 ID

的前 4 个字节来实现

使用 PC 上的软件将输出格式更改为适合 Mifare Ultralight C 卡上 ID 大小的格式(8 Hex?)

改用 Mifare Classic 卡,因为它们有 4 字节 ID

将 7 字节 ID 截断为 4 字节,例如在您的代码中将 bytes.length 更改为 4(对 7 字节 ID 中的前 4 个字节进行硬编码)并处理存在大量(大约 1670 万个)Mifare Ultralight C 的事实看起来 "ID" 与您想要显示的卡片相同的卡片

这是因为亚马逊卖家提供的规格 https://www.amazon.co.uk/Chafon-CF-RS103-Multiple-Support-Compatible-Black/dp/B017VXVZ66(我在制造商网站上找不到任何详细信息)

它说 "Default output 10 digit Dec, control output format through software. "

"Support with windows,linux and android system, but can only set output format in windows pcs.No programming and software required, just plug and play. "

唯一明智的答案是移动所有内容以使用 7 字节 ID。