@@ -11,6 +11,15 @@ class NfcHelper(private val tag: Tag) {
1111
1212 enum class Tech { NfcA , NfcV , Unknown }
1313
14+ /* *
15+ * NFC-A tag information from GET_VERSION command
16+ */
17+ data class NfcATagInfo (
18+ val tagType : String , // e.g., "NTAG213", "NTAG215", "NTAG216", "Unknown"
19+ val userMemoryBytes : Int , // User memory size in bytes
20+ val lastUserPage : Int // Last readable/writable user page
21+ )
22+
1423 fun detectTech (): Tech {
1524 Log .d(" NfcHelper" , " Tag techs: ${tag.techList.joinToString()} " )
1625 return when {
@@ -233,14 +242,19 @@ class NfcHelper(private val tag: Tag) {
233242 // Small delay after connect to let the tag stabilize
234243 Thread .sleep(50 )
235244
245+ // Get tag info to determine memory size
246+ val tagInfo = getTagInfo(nfcA)
247+ Log .d(" NfcHelper" , " Detected ${tagInfo.tagType} : ${tagInfo.userMemoryBytes} bytes, pages 4-${tagInfo.lastUserPage} " )
248+
236249 val startPage = 4
237- val endPage = 129
250+ val endPage = tagInfo.lastUserPage
238251
239252 // Try FAST_READ first (0x3A) - not supported on all devices/tags
240253 try {
241254 val cmd = byteArrayOf(0x3A .toByte(), startPage.toByte(), endPage.toByte())
242255 val resp = nfcA.transceive(cmd)
243256 if (resp.isNotEmpty()) {
257+ Log .d(" NfcHelper" , " FAST_READ succeeded, ${resp.size} bytes" )
244258 return resp
245259 }
246260 } catch (e: IOException ) {
@@ -285,13 +299,74 @@ class NfcHelper(private val tag: Tag) {
285299 }
286300 }
287301
302+ /* *
303+ * Get NFC-A tag information using GET_VERSION command (0x60).
304+ * Works with NTAG21x series tags.
305+ * Falls back to NTAG215 defaults if command fails.
306+ */
307+ private fun getTagInfo (nfcA : NfcA ): NfcATagInfo {
308+ try {
309+ val cmd = byteArrayOf(0x60 .toByte()) // GET_VERSION command
310+ val resp = nfcA.transceive(cmd)
311+
312+ if (resp.size < 8 ) {
313+ Log .w(" NfcHelper" , " GET_VERSION short response: ${resp.size} bytes" )
314+ return NfcATagInfo (" Unknown" , 504 , 129 ) // Default to NTAG215
315+ }
316+
317+ // Response format (8 bytes):
318+ // [0] Fixed header (0x00)
319+ // [1] Vendor ID (0x04 = NXP)
320+ // [2] Product type (0x04 = NTAG)
321+ // [3] Product subtype
322+ // [4] Major product version
323+ // [5] Minor product version
324+ // [6] Storage size (encoded)
325+ // [7] Protocol type
326+
327+ val vendorId = resp[1 ].toInt() and 0xFF
328+ val productType = resp[2 ].toInt() and 0xFF
329+ val storageSize = resp[6 ].toInt() and 0xFF
330+
331+ Log .d(" NfcHelper" , " GET_VERSION: vendor=0x${" %02X" .format(vendorId)} , " +
332+ " type=0x${" %02X" .format(productType)} , storage=0x${" %02X" .format(storageSize)} " )
333+
334+ // Identify tag type based on storage size byte
335+ return when (storageSize) {
336+ 0x0F -> NfcATagInfo (" NTAG213" , 144 , 39 ) // 144 bytes user memory
337+ 0x11 -> NfcATagInfo (" NTAG215" , 504 , 129 ) // 504 bytes user memory
338+ 0x13 -> NfcATagInfo (" NTAG216" , 888 , 225 ) // 888 bytes user memory
339+ 0x10 -> NfcATagInfo (" NTAG210" , 48 , 15 ) // 48 bytes user memory
340+ 0x06 -> NfcATagInfo (" NTAG210u" , 48 , 15 ) // 48 bytes (micro variant)
341+ 0x0E -> NfcATagInfo (" NTAG212" , 128 , 35 ) // 128 bytes user memory
342+ else -> {
343+ Log .w(" NfcHelper" , " Unknown storage size 0x${" %02X" .format(storageSize)} , assuming NTAG215" )
344+ NfcATagInfo (" Unknown (0x${" %02X" .format(storageSize)} )" , 504 , 129 )
345+ }
346+ }
347+ } catch (e: Exception ) {
348+ Log .w(" NfcHelper" , " GET_VERSION failed: ${e.message} , assuming NTAG215" )
349+ return NfcATagInfo (" Unknown" , 504 , 129 ) // Default to NTAG215
350+ }
351+ }
352+
288353 private fun writeNfcAFull (data : ByteArray ): Boolean {
289354 val nfcA = NfcA .get(tag) ? : throw IOException (" NfcA not available" )
290355 nfcA.connect()
291356 try {
292357 // Small delay after connect to let the tag stabilize
293358 Thread .sleep(50 )
294359
360+ // Get tag info to determine memory size
361+ val tagInfo = getTagInfo(nfcA)
362+ Log .d(" NfcHelper" , " Writing to ${tagInfo.tagType} : ${data.size} bytes to write, ${tagInfo.userMemoryBytes} bytes available" )
363+
364+ // Check if data fits on the tag
365+ if (data.size > tagInfo.userMemoryBytes) {
366+ Log .e(" NfcHelper" , " Data too large: ${data.size} bytes > ${tagInfo.userMemoryBytes} bytes available on ${tagInfo.tagType} " )
367+ throw IOException (" Data too large for tag: ${data.size} bytes, but ${tagInfo.tagType} only has ${tagInfo.userMemoryBytes} bytes" )
368+ }
369+
295370 val pageSize = 4
296371 var page = 4
297372 var offset = 0
@@ -313,8 +388,10 @@ class NfcHelper(private val tag: Tag) {
313388 offset + = chunkLen
314389 page + = 1
315390 }
391+ Log .d(" NfcHelper" , " Write complete: ${page - 4 } pages written to ${tagInfo.tagType} " )
316392 return true
317393 } catch (e: IOException ) {
394+ Log .e(" NfcHelper" , " Write exception: ${e.message} " )
318395 e.printStackTrace()
319396 return false
320397 } finally {
0 commit comments