如何正确写入 MifareUltralight NFC 标签?
How to properly write to a MifareUltralight NFC tag?
MifareUltralight.writePage()
方法的正确使用方法是什么?
查询 getMaxTransceiveLength()
方法 returns 253 字节。然而 tag 被宣传为 888 字节。 transceive()
和 writePage()
是否应该被多次调用?正在写入的负载大小为 457 字节。
val jsonString = Gson().toJson(casualty)
val casualtyBytes = toBlob(casualty)
var currentPage = PAGE_OFFSET
val pageBytes = ByteArray(MifareUltralight.PAGE_SIZE)
var byteIndex = 0
for(i in 0 until casualtyBytes.size){
pageBytes[byteIndex] = casualtyBytes[i]
byteIndex++
if(byteIndex == 4 || i == (casualtyBytes.size-1)) {
tag.writePage(currentPage, pageBytes)
currentPage++
byteIndex = 0
}
}
fun toBlob(item : Any) : ByteArray{
val bos = ByteArrayOutputStream()
val gzip = GZIPOutputStream(bos) //compress
val oos = ObjectOutputStream(gzip)
oos.writeObject(item)
oos.close()
return bos.toByteArray()
}
异常
java.io.IOException: Transceive failed
at android.nfc.TransceiveResult.getResponseOrThrow(TransceiveResult.java:52)
at android.nfc.tech.BasicTagTechnology.transceive(BasicTagTechnology.java:151)
at android.nfc.tech.MifareUltralight.writePage(MifareUltralight.java:193)
at some.package.nfc.NfcCasualtyPublisher.writeToTag(NfcCasualtyPublisher.kt:42)
at some.package.nfc.NfcCasualtyPublisher.access$writeToTag(NfcCasualtyPublisher.kt:11)
at some.package.nfc.NfcCasualtyPublisher$publishCasualty.run(NfcCasualtyPublisher.kt:21)
at java.lang.Thread.run(Thread.java:818)
MIFARE Ultralight 和 NTAG 标签的内存以每页 4 字节的页面组织。因此,WRITE 命令 (MifareUltralight.writePage()
) 一次写入 4 个字节。 (注意READ命令(MifareUltralight.readPages()
)一次读取4页(=16字节)
因此,当您要写入 NTAG216 标签时,您需要将数据拆分为 4 个字节的块。您似乎已经在代码中使用 for 循环来做到这一点(尽管您会 运行 遇到一些问题,因为如果您的数据不是页面对齐的,您不会清除最后一页未使用的字节)。
并非 MIFARE Ultralight/NTAG 标签的所有页面都可免费用于数据存储。只有第 4 到 225 页(对于 NTAG216)中的用户存储区是。前 2 页(第 0 页和第 1 页)是只读的,为标签序列号保留。接下来的 2 页(第 2 页和第 3 页)包含一次写入内存(即,一旦写入 1 的位不能再次更改为 0 的内存区域)。具体来说,第 2 页中有锁定位(也在第 226 页中,但如果您的数据只有 457 字节,则不应触及它们)。如果设置锁定位,则可以防止对部分用户内存页面进行写访问,这将导致 "Transceive failed" 异常。因此,如果 PAGE_OFFSET 的值小于 4,您可能通过将数据写入保留内存区域来使标记无法使用。
一般来说,如果您只打算存储(可自由读取的)数据并且不会使用标签的其他功能(例如密码保护),我强烈建议您不要使用低-用于访问 NFC 标签的级别 IO 方法。相反,坚持使用 NDEF 抽象层并将您的数据存储在 NDEF 记录中。 Android 然后会负责将数据放入任何 NFC 标签上的适当内存位置。
最后,收发长度是一个命令或响应中可以传输的字节数。因此,例如,对于 WRITE 命令,这将是总共 6 个字节(4 个字节的数据有效负载、一个地址字节和一个命令代码字节)。对于 READ 响应,这将是 16 字节的数据负载。 getMaxTransceiveLength()
的值表示底层库、HAL和硬件理论上可能的最大收发长度。
MifareUltralight.writePage()
方法的正确使用方法是什么?
查询 getMaxTransceiveLength()
方法 returns 253 字节。然而 tag 被宣传为 888 字节。 transceive()
和 writePage()
是否应该被多次调用?正在写入的负载大小为 457 字节。
val jsonString = Gson().toJson(casualty)
val casualtyBytes = toBlob(casualty)
var currentPage = PAGE_OFFSET
val pageBytes = ByteArray(MifareUltralight.PAGE_SIZE)
var byteIndex = 0
for(i in 0 until casualtyBytes.size){
pageBytes[byteIndex] = casualtyBytes[i]
byteIndex++
if(byteIndex == 4 || i == (casualtyBytes.size-1)) {
tag.writePage(currentPage, pageBytes)
currentPage++
byteIndex = 0
}
}
fun toBlob(item : Any) : ByteArray{
val bos = ByteArrayOutputStream()
val gzip = GZIPOutputStream(bos) //compress
val oos = ObjectOutputStream(gzip)
oos.writeObject(item)
oos.close()
return bos.toByteArray()
}
异常
java.io.IOException: Transceive failed
at android.nfc.TransceiveResult.getResponseOrThrow(TransceiveResult.java:52)
at android.nfc.tech.BasicTagTechnology.transceive(BasicTagTechnology.java:151)
at android.nfc.tech.MifareUltralight.writePage(MifareUltralight.java:193)
at some.package.nfc.NfcCasualtyPublisher.writeToTag(NfcCasualtyPublisher.kt:42)
at some.package.nfc.NfcCasualtyPublisher.access$writeToTag(NfcCasualtyPublisher.kt:11)
at some.package.nfc.NfcCasualtyPublisher$publishCasualty.run(NfcCasualtyPublisher.kt:21)
at java.lang.Thread.run(Thread.java:818)
MIFARE Ultralight 和 NTAG 标签的内存以每页 4 字节的页面组织。因此,WRITE 命令 (MifareUltralight.writePage()
) 一次写入 4 个字节。 (注意READ命令(MifareUltralight.readPages()
)一次读取4页(=16字节)
因此,当您要写入 NTAG216 标签时,您需要将数据拆分为 4 个字节的块。您似乎已经在代码中使用 for 循环来做到这一点(尽管您会 运行 遇到一些问题,因为如果您的数据不是页面对齐的,您不会清除最后一页未使用的字节)。
并非 MIFARE Ultralight/NTAG 标签的所有页面都可免费用于数据存储。只有第 4 到 225 页(对于 NTAG216)中的用户存储区是。前 2 页(第 0 页和第 1 页)是只读的,为标签序列号保留。接下来的 2 页(第 2 页和第 3 页)包含一次写入内存(即,一旦写入 1 的位不能再次更改为 0 的内存区域)。具体来说,第 2 页中有锁定位(也在第 226 页中,但如果您的数据只有 457 字节,则不应触及它们)。如果设置锁定位,则可以防止对部分用户内存页面进行写访问,这将导致 "Transceive failed" 异常。因此,如果 PAGE_OFFSET 的值小于 4,您可能通过将数据写入保留内存区域来使标记无法使用。
一般来说,如果您只打算存储(可自由读取的)数据并且不会使用标签的其他功能(例如密码保护),我强烈建议您不要使用低-用于访问 NFC 标签的级别 IO 方法。相反,坚持使用 NDEF 抽象层并将您的数据存储在 NDEF 记录中。 Android 然后会负责将数据放入任何 NFC 标签上的适当内存位置。
最后,收发长度是一个命令或响应中可以传输的字节数。因此,例如,对于 WRITE 命令,这将是总共 6 个字节(4 个字节的数据有效负载、一个地址字节和一个命令代码字节)。对于 READ 响应,这将是 16 字节的数据负载。 getMaxTransceiveLength()
的值表示底层库、HAL和硬件理论上可能的最大收发长度。