Skip to content

Commit ad6f61f

Browse files
committed
fix: better support for different NFC-A tag sizes
1 parent c383bd3 commit ad6f61f

1 file changed

Lines changed: 78 additions & 1 deletion

File tree

  • app/src/main/java/com/drunkenblindninja/vcoprinttag

app/src/main/java/com/drunkenblindninja/vcoprinttag/NfcHelper.kt

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)