889 NFC Reader Simulation Lab
889.1 Learning Objectives
By the end of this chapter, you will be able to:
- Parse NDEF Messages: Decode TLV containers, record headers, and payload structures
- Identify Tag Types: Recognize NFC Forum Tag Types 1-5 and their characteristics
- Understand Anti-Collision: Explain how NFC readers handle multiple tags in the RF field
- Analyze Security Features: Evaluate authentication, encryption levels, and relay attack vectors
- Implement NFC Simulations: Build software simulations that model NFC reader behavior
What is this chapter? An interactive ESP32-based simulation that demonstrates NFC concepts without requiring physical hardware.
Why simulation? Real NFC communication requires specialized RF hardware operating at 13.56 MHz. This simulation models the logical behavior of NFC systems, helping you understand protocols and data structures.
Prerequisites: - NFC Fundamentals - NDEF format understanding - NFC Tag Programming - Basic programming concepts
What youβll learn: - NDEF message structure and parsing - URI prefix compression - Tag type characteristics - Security and anti-collision protocols
889.2 Prerequisites
Before diving into this lab, you should be familiar with:
- NFC Fundamentals: Understanding NDEF message structure and tag types
- NFC Tag Programming: Basic NFC programming concepts
- C++ Basics: Understanding of Arduino/ESP32 programming for the simulation code
Prerequisites: - NFC Fundamentals - NDEF format basics - NFC Tag Programming - Programming examples
Next Steps: - NFC Real-World Applications - Payments, smart home, authentication - NFC Security and Comparisons - Security deep dive
Practice: - Simulations Hub - More interactive simulations
889.3 NFC Reader Lab: Interactive Simulation
889.3.1 What You Will Learn
This interactive lab simulates an ESP32-based NFC reader system. Since NFC hardware (like the PN532 module) cannot be physically simulated in Wokwi, we demonstrate NFC concepts through software simulation that models:
- NFC Tag Detection: How readers detect and identify tags in range
- NDEF Message Parsing: Decoding the NFC Data Exchange Format structure
- Tag Type Identification: Differentiating between NFC Forum Tag Types 1-5
- URL and Text Records: Processing common NDEF record types
- Security Concepts: Understanding authentication, encryption, and relay attack prevention
Real NFC communication requires specialized RF hardware operating at 13.56 MHz. This simulation models the logical behavior of NFC systems, helping you understand protocols and data structures before working with physical hardware.
889.3.2 Interactive Wokwi Simulation
- Copy the complete code below into the Wokwi editor
- Click the green βPlayβ button to start the simulation
- Open the Serial Monitor (115200 baud) to view output
- The simulation will automatically cycle through NFC scenarios
889.3.3 Complete NFC Simulation Code
Copy this code into the Wokwi ESP32 project:
/*
* NFC Reader Simulation Lab
*
* This educational simulation demonstrates NFC concepts including:
* - NDEF message structure and parsing
* - Tag type identification (Types 1-5)
* - URL and Text record handling
* - Security concepts (authentication, encryption indicators)
* - Anti-collision and tag detection simulation
*
* Author: IoT Class Educational Materials
* License: MIT
*/
#include <Arduino.h>
// =============================================================================
// SECTION 1: NFC CONSTANTS AND DEFINITIONS
// =============================================================================
// NFC Forum Tag Types
enum NFCTagType {
TAG_TYPE_1 = 1, // Topaz, 96-2000 bytes, simple
TAG_TYPE_2 = 2, // NTAG, MIFARE Ultralight, 48-888 bytes
TAG_TYPE_3 = 3, // FeliCa, 1-9 KB, Japan transit
TAG_TYPE_4 = 4, // DESFire, JCOP, up to 32KB, ISO 14443-4
TAG_TYPE_5 = 5 // ICODE SLIX, ISO 15693
};
// NDEF Record Type Name Format (TNF)
enum NDEFTypeName {
TNF_EMPTY = 0x00,
TNF_WELL_KNOWN = 0x01,
TNF_MIME_MEDIA = 0x02,
TNF_ABSOLUTE_URI = 0x03,
TNF_EXTERNAL = 0x04,
TNF_UNKNOWN = 0x05,
TNF_UNCHANGED = 0x06,
TNF_RESERVED = 0x07
};
// Well-Known Record Types
#define RTD_TEXT 'T'
#define RTD_URI 'U'
#define RTD_SMART_POSTER 'S'
// URI Identifier Codes (prefix compression)
const char* URI_PREFIXES[] = {
"", // 0x00 - No prefix
"http://www.", // 0x01
"https://www.", // 0x02
"http://", // 0x03
"https://", // 0x04
"tel:", // 0x05
"mailto:", // 0x06
"ftp://anonymous:anonymous@", // 0x07
"ftp://ftp.", // 0x08
"ftps://", // 0x09
"sftp://", // 0x0A
"smb://", // 0x0B
"nfs://", // 0x0C
"ftp://", // 0x0D
"dav://", // 0x0E
"news:", // 0x0F
"telnet://", // 0x10
"imap:", // 0x11
"rtsp://", // 0x12
"urn:", // 0x13
"pop:", // 0x14
"sip:", // 0x15
"sips:", // 0x16
"tftp:", // 0x17
"btspp://", // 0x18
"btl2cap://", // 0x19
"btgoep://", // 0x1A
"tcpobex://", // 0x1B
"irdaobex://", // 0x1C
"file://", // 0x1D
"urn:epc:id:", // 0x1E
"urn:epc:tag:", // 0x1F
"urn:epc:pat:", // 0x20
"urn:epc:raw:", // 0x21
"urn:epc:", // 0x22
"urn:nfc:" // 0x23
};
#define URI_PREFIX_COUNT 36
// =============================================================================
// SECTION 2: DATA STRUCTURES
// =============================================================================
// NDEF Record Structure
struct NDEFRecord {
uint8_t tnf; // Type Name Format (3 bits)
bool mb; // Message Begin flag
bool me; // Message End flag
bool cf; // Chunk Flag
bool sr; // Short Record (payload < 256 bytes)
bool il; // ID Length present
uint8_t typeLength;
uint32_t payloadLength;
uint8_t idLength;
char type[64];
uint8_t payload[256];
char id[64];
};
// NFC Tag Structure
struct NFCTag {
uint8_t uid[7]; // Unique Identifier (4 or 7 bytes)
uint8_t uidLength;
NFCTagType tagType;
uint16_t memorySize; // Total memory in bytes
uint16_t usedMemory;
bool isLocked;
bool hasPassword;
uint8_t ndefData[512];
uint16_t ndefLength;
char tagName[32];
// Security features
bool hasAuthentication;
bool isEncrypted;
uint8_t securityLevel; // 0=none, 1=password, 2=AES, 3=3DES
};
// NFC Reader State
enum ReaderState {
STATE_IDLE,
STATE_POLLING,
STATE_DETECTED,
STATE_AUTHENTICATING,
STATE_READING,
STATE_WRITING,
STATE_ERROR
};
// =============================================================================
// SECTION 3: SIMULATED NFC TAG DATABASE
// =============================================================================
// Pre-configured simulated tags
NFCTag simulatedTags[5];
int numSimulatedTags = 5;
void initializeSimulatedTags() {
// Tag 1: Simple URL Tag (NTAG213)
simulatedTags[0] = {
.uid = {0x04, 0xA3, 0xB2, 0xC1, 0xD4, 0x5E, 0x80},
.uidLength = 7,
.tagType = TAG_TYPE_2,
.memorySize = 144,
.usedMemory = 45,
.isLocked = false,
.hasPassword = false,
.ndefData = {0},
.ndefLength = 0,
.tagName = "URL Tag (NTAG213)",
.hasAuthentication = false,
.isEncrypted = false,
.securityLevel = 0
};
// Create NDEF message for URL tag
// NDEF TLV: Type=03, Length, NDEF Message
uint8_t urlNdef[] = {
0x03, 0x14, // NDEF TLV, length 20
0xD1, // MB=1, ME=1, CF=0, SR=1, IL=0, TNF=01
0x01, // Type Length = 1
0x10, // Payload Length = 16
0x55, // Type = 'U' (URI)
0x04, // URI prefix: https://
'i', 'o', 't', 'c', 'l', 'a', 's', 's', // Payload
'.', 'e', 'x', 'a', 'm', 'p', 'l', 'e',
0xFE // Terminator TLV
};
memcpy(simulatedTags[0].ndefData, urlNdef, sizeof(urlNdef));
simulatedTags[0].ndefLength = sizeof(urlNdef);
// Tag 2: Text Record Tag (NTAG216)
simulatedTags[1] = {
.uid = {0x04, 0xE7, 0xF8, 0x92, 0x1A, 0x3C, 0x70},
.uidLength = 7,
.tagType = TAG_TYPE_2,
.memorySize = 888,
.usedMemory = 128,
.isLocked = false,
.hasPassword = true,
.ndefData = {0},
.ndefLength = 0,
.tagName = "Text Tag (NTAG216)",
.hasAuthentication = false,
.isEncrypted = false,
.securityLevel = 1
};
// NDEF message with text record
uint8_t textNdef[] = {
0x03, 0x21, // NDEF TLV, length 33
0xD1, // MB=1, ME=1, CF=0, SR=1, IL=0, TNF=01
0x01, // Type Length = 1
0x1D, // Payload Length = 29
0x54, // Type = 'T' (Text)
0x02, // Status byte: UTF-8, lang len=2
'e', 'n', // Language code: English
'W', 'e', 'l', 'c', 'o', 'm', 'e', ' ',
't', 'o', ' ', 'I', 'o', 'T', ' ', 'C',
'l', 'a', 's', 's', ' ', 'L', 'a', 'b', '!',
0xFE // Terminator TLV
};
memcpy(simulatedTags[1].ndefData, textNdef, sizeof(textNdef));
simulatedTags[1].ndefLength = sizeof(textNdef);
// Tag 3: Smart Poster (NTAG215)
simulatedTags[2] = {
.uid = {0x04, 0xB5, 0xC6, 0xD7, 0xE8, 0xF9, 0x0A},
.uidLength = 7,
.tagType = TAG_TYPE_2,
.memorySize = 504,
.usedMemory = 256,
.isLocked = true,
.hasPassword = false,
.ndefData = {0},
.ndefLength = 0,
.tagName = "Smart Poster (NTAG215)",
.hasAuthentication = false,
.isEncrypted = false,
.securityLevel = 0
};
// Smart poster with URL and title
uint8_t posterNdef[] = {
0x03, 0x35, // NDEF TLV, length 53
// Record 1: Smart Poster (container)
0x91, // MB=1, ME=0, CF=0, SR=1, IL=0, TNF=01
0x02, // Type Length = 2
0x2F, // Payload Length = 47
'S', 'p', // Type = "Sp" (Smart Poster)
// Nested Record 1: URI
0x91, // MB=1, ME=0
0x01, 0x10, 0x55, 0x04, // URI record
'g', 'i', 't', 'h', 'u', 'b', '.', 'c', 'o', 'm',
'/', 'i', 'o', 't',
// Nested Record 2: Title
0x51, // MB=0, ME=1
0x01, 0x0F, 0x54, 0x02, // Text record
'e', 'n',
'I', 'o', 'T', ' ', 'P', 'r', 'o', 'j', 'e', 'c', 't',
0xFE // Terminator
};
memcpy(simulatedTags[2].ndefData, posterNdef, sizeof(posterNdef));
simulatedTags[2].ndefLength = sizeof(posterNdef);
// Tag 4: Secure Access Card (MIFARE DESFire)
simulatedTags[3] = {
.uid = {0x04, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC},
.uidLength = 7,
.tagType = TAG_TYPE_4,
.memorySize = 8192,
.usedMemory = 512,
.isLocked = false,
.hasPassword = true,
.ndefData = {0},
.ndefLength = 0,
.tagName = "Access Card (DESFire)",
.hasAuthentication = true,
.isEncrypted = true,
.securityLevel = 3
};
// Secure tag with encrypted payload indicator
uint8_t secureNdef[] = {
0x03, 0x28, // NDEF TLV
0xD2, // TNF=02 (MIME type)
0x18, // Type length = 24
0x0C, // Payload length = 12
'a', 'p', 'p', 'l', 'i', 'c', 'a', 't', 'i', 'o', 'n', '/',
'v', 'n', 'd', '.', 'a', 'c', 'c', 'e', 's', 's', '-', 'c',
0xAE, 0x7B, 0xC2, 0x8F, 0x51, 0xD4, 0x33, 0x19, // Encrypted data
0x8A, 0xE2, 0x1C, 0x44,
0xFE
};
memcpy(simulatedTags[3].ndefData, secureNdef, sizeof(secureNdef));
simulatedTags[3].ndefLength = sizeof(secureNdef);
// Tag 5: vCard Contact (NTAG424)
simulatedTags[4] = {
.uid = {0x04, 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA},
.uidLength = 7,
.tagType = TAG_TYPE_4,
.memorySize = 416,
.usedMemory = 280,
.isLocked = false,
.hasPassword = true,
.ndefData = {0},
.ndefLength = 0,
.tagName = "Contact Card (NTAG424)",
.hasAuthentication = true,
.isEncrypted = false,
.securityLevel = 2
};
// vCard MIME type record
uint8_t vcardNdef[] = {
0x03, 0x42, // NDEF TLV
0xD2, // TNF=02 (MIME type)
0x0A, // Type length = 10
0x34, // Payload length = 52
't', 'e', 'x', 't', '/', 'v', 'c', 'a', 'r', 'd',
'B', 'E', 'G', 'I', 'N', ':', 'V', 'C', 'A', 'R', 'D', '\n',
'F', 'N', ':', 'I', 'o', 'T', ' ', 'L', 'a', 'b', '\n',
'T', 'E', 'L', ':', '+', '1', '5', '5', '5', '1', '2', '3', '4', '\n',
'E', 'N', 'D', ':', 'V', 'C', 'A', 'R', 'D',
0xFE
};
memcpy(simulatedTags[4].ndefData, vcardNdef, sizeof(vcardNdef));
simulatedTags[4].ndefLength = sizeof(vcardNdef);
}
// =============================================================================
// SECTION 4: NFC READER SIMULATION CLASS
// =============================================================================
class NFCReaderSimulator {
private:
ReaderState state;
int currentTagIndex;
unsigned long lastPollTime;
unsigned long pollInterval;
bool verboseMode;
public:
NFCReaderSimulator() {
state = STATE_IDLE;
currentTagIndex = -1;
lastPollTime = 0;
pollInterval = 3000; // 3 seconds between tag reads
verboseMode = true;
}
void begin() {
Serial.println("\n" + String('=', 60));
Serial.println(" NFC READER SIMULATION - IoT Class Lab");
Serial.println(String('=', 60));
Serial.println("\nInitializing simulated PN532 NFC reader...");
delay(500);
// Simulate hardware initialization
printProgress("Checking I2C connection", 300);
printProgress("Loading firmware version 1.6", 200);
printProgress("Configuring RF field", 300);
printProgress("Setting ISO14443A mode", 200);
Serial.println("\n[OK] NFC Reader initialized successfully!");
Serial.println(" Hardware: Simulated PN532 via I2C");
Serial.println(" Firmware: v1.6 (simulation)");
Serial.println(" Frequency: 13.56 MHz");
Serial.println(" Protocols: ISO14443A/B, ISO15693, FeliCa");
Serial.println("\n[INFO] Starting tag polling...\n");
state = STATE_POLLING;
}
void printProgress(const char* message, int delayMs) {
Serial.print(" ");
Serial.print(message);
Serial.print("...");
delay(delayMs);
Serial.println(" OK");
}
void poll() {
if (millis() - lastPollTime < pollInterval) {
return;
}
lastPollTime = millis();
// Cycle through simulated tags
currentTagIndex = (currentTagIndex + 1) % numSimulatedTags;
Serial.println("\n" + String('-', 60));
Serial.println("POLLING: Searching for NFC tags...");
delay(200);
// Simulate RF field activation and detection
simulateTagDetection(&simulatedTags[currentTagIndex]);
}
void simulateTagDetection(NFCTag* tag) {
state = STATE_DETECTED;
Serial.println("\n[DETECTED] NFC Tag Found!");
Serial.println(String('-', 40));
// Print UID
Serial.print("UID: ");
for (int i = 0; i < tag->uidLength; i++) {
if (i > 0) Serial.print(":");
if (tag->uid[i] < 0x10) Serial.print("0");
Serial.print(tag->uid[i], HEX);
}
Serial.println();
// Print tag info
Serial.print("Tag Name: ");
Serial.println(tag->tagName);
Serial.print("Tag Type: NFC Forum Type ");
Serial.println(tag->tagType);
Serial.print("Memory: ");
Serial.print(tag->usedMemory);
Serial.print("/");
Serial.print(tag->memorySize);
Serial.println(" bytes");
// Security info
printSecurityInfo(tag);
// Handle authentication if needed
if (tag->hasAuthentication) {
simulateAuthentication(tag);
}
// Read NDEF data
state = STATE_READING;
readNDEFMessage(tag);
state = STATE_POLLING;
}
void printSecurityInfo(NFCTag* tag) {
Serial.println("\n[SECURITY]");
Serial.print(" Locked: ");
Serial.println(tag->isLocked ? "Yes (Read-Only)" : "No (Read/Write)");
Serial.print(" Password Protected: ");
Serial.println(tag->hasPassword ? "Yes" : "No");
Serial.print(" Authentication: ");
Serial.println(tag->hasAuthentication ? "Required" : "Not Required");
Serial.print(" Encryption: ");
Serial.println(tag->isEncrypted ? "Yes" : "No");
Serial.print(" Security Level: ");
switch(tag->securityLevel) {
case 0: Serial.println("0 - None"); break;
case 1: Serial.println("1 - Password (32-bit)"); break;
case 2: Serial.println("2 - AES-128"); break;
case 3: Serial.println("3 - 3DES/AES-256"); break;
}
}
void simulateAuthentication(NFCTag* tag) {
Serial.println("\n[AUTHENTICATION]");
state = STATE_AUTHENTICATING;
if (tag->securityLevel >= 2) {
Serial.println(" Initiating mutual authentication...");
delay(100);
Serial.print(" Generating random challenge: ");
for (int i = 0; i < 8; i++) {
Serial.print(random(256), HEX);
}
Serial.println();
delay(150);
Serial.println(" Verifying cryptographic response...");
delay(100);
Serial.println(" [OK] Authentication successful!");
} else if (tag->hasPassword) {
Serial.println(" Using default password for simulation...");
delay(100);
Serial.println(" [OK] Password accepted!");
}
}
void readNDEFMessage(NFCTag* tag) {
Serial.println("\n[NDEF MESSAGE]");
if (tag->ndefLength == 0) {
Serial.println(" No NDEF data found on tag.");
return;
}
// Parse TLV structure
int pos = 0;
while (pos < tag->ndefLength) {
uint8_t tlvType = tag->ndefData[pos++];
if (tlvType == 0x00) {
// NULL TLV - skip
continue;
} else if (tlvType == 0xFE) {
// Terminator TLV
Serial.println(" [END] NDEF Terminator TLV");
break;
} else if (tlvType == 0x03) {
// NDEF Message TLV
uint8_t length = tag->ndefData[pos++];
Serial.print(" NDEF TLV: Length = ");
Serial.print(length);
Serial.println(" bytes");
// Parse NDEF records
parseNDEFRecords(&tag->ndefData[pos], length);
pos += length;
}
}
}
void parseNDEFRecords(uint8_t* data, int length) {
int pos = 0;
int recordNum = 1;
while (pos < length) {
// Parse record header
uint8_t header = data[pos++];
bool mb = (header & 0x80) != 0; // Message Begin
bool me = (header & 0x40) != 0; // Message End
bool cf = (header & 0x20) != 0; // Chunk Flag
bool sr = (header & 0x10) != 0; // Short Record
bool il = (header & 0x08) != 0; // ID Length present
uint8_t tnf = header & 0x07; // Type Name Format
uint8_t typeLength = data[pos++];
uint32_t payloadLength = sr ? data[pos++] :
((uint32_t)data[pos] << 24) | ((uint32_t)data[pos+1] << 16) |
((uint32_t)data[pos+2] << 8) | data[pos+3];
if (!sr) pos += 4;
uint8_t idLength = il ? data[pos++] : 0;
Serial.println();
Serial.print(" Record #");
Serial.print(recordNum++);
Serial.println(":");
Serial.print(" Flags: MB=");
Serial.print(mb);
Serial.print(" ME=");
Serial.print(me);
Serial.print(" SR=");
Serial.print(sr);
Serial.print(" TNF=");
Serial.println(tnf);
// Get type
char type[65] = {0};
for (int i = 0; i < typeLength && i < 64; i++) {
type[i] = data[pos++];
}
// Skip ID if present
pos += idLength;
Serial.print(" Type: ");
printTNFDescription(tnf);
Serial.print(" - \"");
Serial.print(type);
Serial.println("\"");
Serial.print(" Payload Length: ");
Serial.print(payloadLength);
Serial.println(" bytes");
// Parse payload based on type
if (tnf == TNF_WELL_KNOWN) {
if (type[0] == RTD_URI) {
parseURIPayload(&data[pos], payloadLength);
} else if (type[0] == RTD_TEXT) {
parseTextPayload(&data[pos], payloadLength);
} else if (type[0] == 'S' && type[1] == 'p') {
Serial.println(" [SMART POSTER - Contains nested records]");
parseNDEFRecords(&data[pos], payloadLength);
}
} else if (tnf == TNF_MIME_MEDIA) {
parseMIMEPayload(type, &data[pos], payloadLength);
}
pos += payloadLength;
if (me) break; // Last record
}
}
void printTNFDescription(uint8_t tnf) {
switch(tnf) {
case TNF_EMPTY: Serial.print("Empty"); break;
case TNF_WELL_KNOWN: Serial.print("Well-Known"); break;
case TNF_MIME_MEDIA: Serial.print("MIME-Type"); break;
case TNF_ABSOLUTE_URI: Serial.print("Absolute-URI"); break;
case TNF_EXTERNAL: Serial.print("External"); break;
case TNF_UNKNOWN: Serial.print("Unknown"); break;
case TNF_UNCHANGED: Serial.print("Unchanged"); break;
default: Serial.print("Reserved"); break;
}
}
void parseURIPayload(uint8_t* payload, int length) {
uint8_t prefixCode = payload[0];
Serial.print(" URI Prefix Code: 0x");
if (prefixCode < 0x10) Serial.print("0");
Serial.println(prefixCode, HEX);
String uri = "";
if (prefixCode < URI_PREFIX_COUNT) {
uri = URI_PREFIXES[prefixCode];
}
for (int i = 1; i < length; i++) {
uri += (char)payload[i];
}
Serial.print(" Full URI: ");
Serial.println(uri);
// Analyze URI type
Serial.print(" URI Type: ");
if (uri.startsWith("https://")) {
Serial.println("Secure Web Link (HTTPS)");
} else if (uri.startsWith("http://")) {
Serial.println("Web Link (HTTP) - Not secure!");
} else if (uri.startsWith("tel:")) {
Serial.println("Phone Number");
} else if (uri.startsWith("mailto:")) {
Serial.println("Email Address");
} else {
Serial.println("Other URI Scheme");
}
}
void parseTextPayload(uint8_t* payload, int length) {
uint8_t statusByte = payload[0];
bool utf16 = (statusByte & 0x80) != 0;
uint8_t langLength = statusByte & 0x3F;
Serial.print(" Encoding: ");
Serial.println(utf16 ? "UTF-16" : "UTF-8");
String lang = "";
for (int i = 0; i < langLength; i++) {
lang += (char)payload[1 + i];
}
Serial.print(" Language: ");
Serial.println(lang);
String text = "";
for (int i = 1 + langLength; i < length; i++) {
text += (char)payload[i];
}
Serial.print(" Text: \"");
Serial.print(text);
Serial.println("\"");
}
void parseMIMEPayload(const char* mimeType, uint8_t* payload, int length) {
Serial.print(" MIME Type: ");
Serial.println(mimeType);
if (strstr(mimeType, "vcard") != NULL) {
Serial.println(" [vCard Contact Data]");
Serial.print(" Content: ");
for (int i = 0; i < length && i < 80; i++) {
if (payload[i] >= 32 && payload[i] < 127) {
Serial.print((char)payload[i]);
} else {
Serial.print(".");
}
}
Serial.println();
} else if (strstr(mimeType, "access") != NULL) {
Serial.println(" [Access Control Data - Encrypted]");
Serial.print(" Encrypted Bytes: ");
for (int i = 0; i < length && i < 16; i++) {
if (payload[i] < 0x10) Serial.print("0");
Serial.print(payload[i], HEX);
Serial.print(" ");
}
Serial.println();
} else {
Serial.print(" Raw Data (");
Serial.print(length);
Serial.println(" bytes)");
}
}
void demonstrateAntiCollision() {
Serial.println("\n" + String('=', 60));
Serial.println("DEMONSTRATION: NFC Anti-Collision");
Serial.println(String('=', 60));
Serial.println("\nWhen multiple tags are in RF field simultaneously:");
Serial.println("1. Reader sends REQA (Request Type A) command");
Serial.println("2. All tags respond with ATQA (Answer To Request)");
Serial.println("3. Reader initiates anti-collision loop:");
Serial.println(" - Sends SELECT command with partial UID");
Serial.println(" - Tags with matching bits respond");
Serial.println(" - Collisions detected via Manchester encoding");
Serial.println(" - Reader narrows down bit-by-bit");
Serial.println("4. Single tag selected, others remain quiet");
Serial.println("5. Process repeats for remaining tags");
delay(500);
Serial.println("\nSimulating multi-tag detection...");
delay(300);
Serial.println("\n[RF FIELD] Activated at 13.56 MHz");
delay(200);
Serial.println("[REQA] Sent: 0x26");
delay(100);
Serial.println("[ATQA] Received: 0x0004 (NTAG family)");
delay(100);
Serial.println("[COLLISION] Detected at bit position 24");
delay(150);
Serial.println("[SELECT] NVB=40, UID prefix: 04:A3:B2:C1");
delay(100);
Serial.println("[SAK] Received: 0x00 (UID complete)");
delay(100);
Serial.println("[SELECTED] Tag 1 active, others halted");
Serial.println("\n[OK] Anti-collision complete - 1 tag selected");
}
void demonstrateRelayAttack() {
Serial.println("\n" + String('=', 60));
Serial.println("SECURITY: Relay Attack Demonstration");
Serial.println(String('=', 60));
Serial.println("\nRelay Attack Concept:");
Serial.println("Attacker uses two devices to extend NFC range:");
Serial.println("");
Serial.println(" [Victim Card] <--NFC--> [Attacker A]");
Serial.println(" |");
Serial.println(" (relay link)");
Serial.println(" |");
Serial.println(" [Payment Terminal] <--NFC--> [Attacker B]");
Serial.println("");
Serial.println("Timeline of attack:");
Serial.println(" T+0ms: Terminal activates, requests payment");
Serial.println(" T+5ms: Attacker B relays request to Attacker A");
Serial.println(" T+50ms: Attacker A presents request to victim");
Serial.println(" T+55ms: Victim's card responds (auto-response)");
Serial.println(" T+60ms: Response relayed back to terminal");
Serial.println(" T+65ms: Terminal accepts payment");
Serial.println("");
Serial.println("Detection/Prevention Methods:");
Serial.println(" 1. Distance bounding: Measure round-trip time");
Serial.println(" - Light travels ~30cm in 1ns");
Serial.println(" - NFC range: ~10cm = ~0.3ns");
Serial.println(" - Relay adds >100ns delay = DETECTED!");
Serial.println("");
Serial.println(" 2. Require user interaction:");
Serial.println(" - Biometric (fingerprint/face)");
Serial.println(" - PIN entry");
Serial.println(" - Button press");
Serial.println("");
Serial.println(" 3. Payment limits for contactless:");
Serial.println(" - UK: GBP 100 limit without PIN");
Serial.println(" - US: $100-200 typical limit");
Serial.println("");
Serial.println("[!] Always require authentication for high-value NFC operations!");
}
void printTagTypeInfo() {
Serial.println("\n" + String('=', 60));
Serial.println("NFC FORUM TAG TYPES REFERENCE");
Serial.println(String('=', 60));
Serial.println("\nType 1 (TOPAZ, Broadcom):");
Serial.println(" Memory: 96 - 2000 bytes");
Serial.println(" Read/Write: Yes (lockable)");
Serial.println(" Speed: 106 kbps");
Serial.println(" Use: Low-cost tags, one-time URLs");
Serial.println("\nType 2 (NTAG, MIFARE Ultralight):");
Serial.println(" Memory: 48 - 888 bytes");
Serial.println(" Read/Write: Yes (optional password)");
Serial.println(" Speed: 106 kbps");
Serial.println(" Use: Smart posters, IoT triggers");
Serial.println(" Examples: NTAG213 (144B), NTAG215 (504B), NTAG216 (888B)");
Serial.println("\nType 3 (FeliCa, Sony):");
Serial.println(" Memory: 1 - 9 KB");
Serial.println(" Read/Write: Yes");
Serial.println(" Speed: 212/424 kbps");
Serial.println(" Use: Japan transit (Suica), e-wallets");
Serial.println("\nType 4 (DESFire, JCOP, ISO 14443-4):");
Serial.println(" Memory: Up to 32 KB");
Serial.println(" Read/Write: Yes");
Serial.println(" Speed: 106-848 kbps");
Serial.println(" Security: AES-128, 3DES encryption");
Serial.println(" Use: Access control, payments, secure ID");
Serial.println(" Examples: MIFARE DESFire, NTAG424");
Serial.println("\nType 5 (ICODE SLIX, ISO 15693):");
Serial.println(" Memory: Up to 8 KB");
Serial.println(" Read/Write: Yes");
Serial.println(" Speed: 6.6 - 26 kbps (slower)");
Serial.println(" Range: Up to 1 meter (extended)");
Serial.println(" Use: Library books, industrial RFID");
}
};
// =============================================================================
// SECTION 5: GLOBAL OBJECTS AND SETUP
// =============================================================================
NFCReaderSimulator nfcReader;
unsigned long demoStartTime;
int demoPhase = 0;
void setup() {
Serial.begin(115200);
while (!Serial) delay(10);
delay(1000);
// Initialize simulated tag database
initializeSimulatedTags();
// Initialize reader
nfcReader.begin();
demoStartTime = millis();
}
void loop() {
unsigned long elapsed = millis() - demoStartTime;
// Run through demonstration phases
switch(demoPhase) {
case 0:
// Phase 0: Poll through all tags (0-18 seconds)
if (elapsed < 18000) {
nfcReader.poll();
} else {
demoPhase = 1;
}
break;
case 1:
// Phase 1: Show tag type reference
nfcReader.printTagTypeInfo();
delay(3000);
demoPhase = 2;
break;
case 2:
// Phase 2: Anti-collision demo
nfcReader.demonstrateAntiCollision();
delay(2000);
demoPhase = 3;
break;
case 3:
// Phase 3: Security demo
nfcReader.demonstrateRelayAttack();
delay(2000);
demoPhase = 4;
break;
case 4:
// Phase 4: Summary and restart
Serial.println("\n" + String('=', 60));
Serial.println("LAB COMPLETE - SUMMARY");
Serial.println(String('=', 60));
Serial.println("\nKey concepts demonstrated:");
Serial.println(" [x] NDEF message structure and TLV parsing");
Serial.println(" [x] URI and Text record decoding");
Serial.println(" [x] Tag type identification (Types 1-5)");
Serial.println(" [x] Security levels and authentication");
Serial.println(" [x] Anti-collision protocol");
Serial.println(" [x] Relay attack awareness");
Serial.println("\nRestarting demonstration in 10 seconds...\n");
delay(10000);
demoPhase = 0;
demoStartTime = millis();
break;
}
delay(100); // Small delay for loop
}
// =============================================================================
// SECTION 6: UTILITY FUNCTIONS
// =============================================================================
// Calculate NDEF message checksum (for validation)
uint8_t calculateNDEFChecksum(uint8_t* data, int length) {
uint8_t checksum = 0;
for (int i = 0; i < length; i++) {
checksum ^= data[i];
}
return checksum;
}
// Format UID for display
String formatUID(uint8_t* uid, int length) {
String result = "";
for (int i = 0; i < length; i++) {
if (i > 0) result += ":";
if (uid[i] < 0x10) result += "0";
result += String(uid[i], HEX);
}
result.toUpperCase();
return result;
}
// Convert byte array to hex string
String bytesToHex(uint8_t* bytes, int length) {
String result = "";
for (int i = 0; i < length; i++) {
if (bytes[i] < 0x10) result += "0";
result += String(bytes[i], HEX);
}
result.toUpperCase();
return result;
}889.4 Understanding the Code
The simulation demonstrates these key NFC concepts:
889.5 Challenge Exercises
889.6 Expected Outcomes
After completing this lab, you should be able to:
Explain NDEF structure: Describe the TLV container format, record headers, and payload organization
Parse NFC data: Read and interpret raw NDEF bytes including:
- Type Name Format (TNF) classification
- Record type identification (U, T, Sp, MIME)
- Payload decoding for URI and Text records
Identify tag types: Recognize NFC Forum Tag Types 1-5 and their characteristics:
- Memory capacity ranges
- Security features (none, password, crypto)
- Appropriate use cases
Understand security: Explain NFC security concepts including:
- Authentication mechanisms
- Encryption levels (none, AES, 3DES)
- Relay attack vectors and mitigations
- Distance bounding protocols
Design NFC applications: Select appropriate tag types and record formats for:
- Smart posters and marketing
- Access control and authentication
- Device pairing and IoT triggers
- Payment and secure transactions
After mastering this simulation, practice with real hardware:
- Arduino + PN532: Use the Adafruit PN532 library with actual NFC tags
- Mobile development: Build Android NFC apps using the NfcAdapter API
- Security testing: Explore tools like Proxmark3 for NFC security research
889.7 Summary
This lab demonstrated NFC concepts through interactive simulation:
- NDEF Message Parsing: TLV container structure, record headers with MB/ME/CF/SR/IL/TNF flags, and payload processing for URI/Text/MIME types
- URI Prefix Compression: How NFC saves space using 36 standardized prefix codes (0x01-0x23) to compress common URL schemes
- Tag Type Identification: Characteristics of NFC Forum Types 1-5 including memory capacity, security levels, and appropriate use cases
- Anti-Collision Protocol: REQA/ATQA/SELECT sequence for handling multiple tags using bit-by-bit UID resolution
- Security Demonstrations: Relay attack mechanics, distance bounding countermeasures, and authentication requirements
889.8 Whatβs Next
The next chapter, NFC Real-World Applications, explores practical NFC implementations including mobile payments (Apple Pay/Google Pay), smart home automation with NFC tags, product authentication and anti-counterfeiting, and Python-based security analysis tools.