29  NFC IoT Integration and Labs

Key Concepts
  • NFC-to-Cloud: The integration path from NFC tag scan → smartphone/reader → internet → cloud API; used for asset tracking, authentication, and data collection
  • REST API: A web service interface used by NFC-enabled IoT gateways to upload scan events to cloud platforms
  • Webhook: A callback URL registered on a cloud platform that receives HTTP POST notifications when an NFC event occurs
  • Tag UID Lookup: Resolving an NFC tag’s unique identifier (UID) to an asset record in a back-end database, linking physical and digital identities
  • Gateway NFC Reader: A fixed NFC reader integrated into a gateway device, enabling automatic scanning as assets pass through a defined location
  • NFC + BLE Hybrid: Combining NFC for initial pairing/provisioning with BLE for ongoing data communication; a common pattern for IoT device setup
  • Digital Twin: A cloud-side representation of a physical asset, updated with data each time the asset’s NFC tag is scanned

29.1 In 60 Seconds

NFC integrates into IoT ecosystems through gateway patterns that connect tag scans to cloud services via MQTT. This chapter includes two complete labs: an ESP32-based NFC door lock with UID authorization and servo control, and a Raspberry Pi Python smart home server with scene management, MQTT integration, and tag-to-action mappings.

Sammy the Sensor was curious: “How does tapping a sticker control things across the whole house?” Max the Microcontroller explained, “Think of it like a relay race. You tap the NFC tag, which tells the reader what to do. The reader sends a message through MQTT – that is like a postal service for machines – and all the smart devices in your house get the message at once!” Bella the Battery loved the door lock project: “We build a real door lock! When you tap your badge, the system checks if you are allowed in. If yes, the servo motor turns and the door opens. If not, a red light flashes and a buzzer sounds. It is like having a robot bouncer!” Lila the LED added, “And the Raspberry Pi server remembers every tap, so you always know who came in and when.”

29.2 Learning Objectives

By the end of this chapter, you will be able to:

  • Construct NFC Gateway Pipelines: Design and build NFC-to-cloud data flows using MQTT for IoT asset tracking
  • Develop ESP32 Access Control: Implement a working NFC door lock system with UID authorization, servo control, and circular-buffer logging
  • Architect Smart Home Automation: Create a Raspberry Pi NFC server with scene management, tag-to-action mapping, and MQTT device control
  • Evaluate Local vs Cloud Architectures: Compare local-intelligence and cloud-first NFC integration patterns and justify hybrid approaches for production IoT systems

NFC (Near Field Communication) can be used to configure IoT devices with a simple tap – hold your phone against a sensor to set up its Wi-Fi credentials, or tap a smart tag to trigger an automation. This chapter explores how NFC integrates with broader IoT systems as a convenient setup and interaction method.

29.3 Prerequisites

Before diving into this chapter, you should be familiar with:

NFC Deep Dives:

IoT Protocols:


29.4 NFC in IoT Ecosystems

29.4.1 IoT Gateway Pattern

NFC gateway pattern diagram showing asset tags scanned by NFC readers at checkpoints, data published to MQTT broker, and forwarded to cloud IoT hub for asset tracking and analytics
Figure 29.1: NFC gateway pattern connecting asset tags to cloud via IoT hub

NFC gateway throughput limits scale with scan frequency. For a factory checkpoint:

\[ \text{Max throughput}_{\text{workers/min}} = \frac{60 \text{ sec/min}}{T_{\text{scan}} + T_{\text{delay}}} \]

Typical scan: NFC read (80 ms) + MQTT publish (150 ms) + display update (20 ms) + worker delay (250 ms buffer) = 500 ms per scan. Max throughput: 60 / 0.5 = 120 workers/min per reader.

Peak entry scenario: 1,000-worker factory with 8 AM shift start, 80% arrive in 15-minute window (800 workers). Required readers: 800 workers / 15 min / 120 workers/min = 0.44 readers (1 reader sufficient with 56% utilization). Adding 2nd reader enables redundancy and splits the line, reducing perceived wait time from 30 s average to 15 s.

Try It: NFC Gateway Throughput Calculator

Example: Smart Factory Asset Tracking

# Requires paho-mqtt 2.0+
import nfc
import paho.mqtt.client as mqtt
import json
from datetime import datetime

# Connect to NFC reader and MQTT broker
clf = nfc.ContactlessFrontend('usb')
mqtt_client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="nfc-gateway")
mqtt_client.connect("mqtt.factory.local", 1883)

def on_tag_scan(tag):
    """Process scanned asset tag"""
    asset_id = tag.identifier.hex()

    # Read asset metadata from tag
    if tag.ndef:
        asset_name = tag.ndef.records[0].text
    else:
        asset_name = "Unknown"

    # Publish to MQTT
    data = {
        "asset_id": asset_id,
        "asset_name": asset_name,
        "location": "Assembly Line 3",
        "timestamp": datetime.now().isoformat(),
        "scanned_by": "Worker-042"
    }

    mqtt_client.publish("factory/assets/scans", json.dumps(data))
    print(f"Asset {asset_name} tracked at Assembly Line 3")

    return True

# Listen for tags
print("Factory asset tracking active...")
clf.connect(rdwr={'on-connect': on_tag_scan})

Mid-Chapter Check: How does the gateway pattern work?

29.5 Hands-On Labs

29.5.1 Lab 1: ESP32 NFC Access Control System

Build an NFC-based door lock system using ESP32 and PN532 module.

Hardware Required:

  • ESP32 Development Board
  • PN532 NFC Reader Module (I2C)
  • Servo motor (SG90) for lock mechanism
  • NFC tags (Type 2 recommended)
  • LED (Green for granted, Red for denied)
  • Buzzer (optional)
  • Jumper wires
  • Breadboard

Wiring Diagram:

PN532 NFC Module → ESP32
  VCC → 3.3V
  GND → GND
  SDA → GPIO 21 (I2C SDA)
  SCL → GPIO 22 (I2C SCL)

Servo Motor → ESP32
  Signal → GPIO 13
  VCC → 5V (via external power if needed)
  GND → GND

Green LED → GPIO 25 (via 220Ω resistor)
Red LED → GPIO 26 (via 220Ω resistor)
Buzzer → GPIO 27

Complete Code:

#include <Wire.h>
#include <PN532_I2C.h>
#include <PN532.h>
#include <NfcAdapter.h>
#include <ESP32Servo.h>

// Pin definitions
#define SERVO_PIN 13
#define GREEN_LED 25
#define RED_LED 26
#define BUZZER 27

// NFC setup
PN532_I2C pn532i2c(Wire);
NfcAdapter nfc = NfcAdapter(pn532i2c);

// Servo for lock
Servo lockServo;

// Authorized UIDs (add your NFC tag UIDs here)
String authorizedUIDs[] = {
  "04 A3 B2 C1 D4 5E 80",  // Alice's badge
  "08 F7 E2 9A 3B 1C 4D",  // Bob's badge
  "12 34 56 78 9A BC DE"   // Admin card
};
const int numAuthorizedUIDs = 3;

// Lock states
const int LOCKED_ANGLE = 0;
const int UNLOCKED_ANGLE = 90;
bool isLocked = true;

// Access log (circular buffer)
struct AccessLog {
  String uid;
  String status;
  unsigned long timestamp;
};
AccessLog accessHistory[10];
int accessLogIndex = 0;

void setup() {
  Serial.begin(115200);
  Serial.println("\n=== ESP32 NFC Access Control System ===");

  // Initialize pins
  pinMode(GREEN_LED, OUTPUT);
  pinMode(RED_LED, OUTPUT);
  pinMode(BUZZER, OUTPUT);

  // Initial state: deny
  digitalWrite(GREEN_LED, LOW);
  digitalWrite(RED_LED, HIGH);

  // Initialize servo
  lockServo.attach(SERVO_PIN);
  lockServo.write(LOCKED_ANGLE);

  // Initialize NFC
  nfc.begin();
  Serial.println("✓ NFC Reader initialized");
  Serial.println("📱 Ready to scan NFC tags...\n");

  // Startup beep
  beep(100);
}

void loop() {
  // Check for NFC tag
  if (nfc.tagPresent(1000)) {  // 1 second timeout
    NfcTag tag = nfc.read();

    String uid = tag.getUidString();
    Serial.println("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
    Serial.println("📱 NFC Tag Detected");
    Serial.print("   UID: ");
    Serial.println(uid);

    // Check authorization
    if (isAuthorized(uid)) {
      grantAccess(uid);
    } else {
      denyAccess(uid);
    }

    Serial.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");

    delay(2000);  // Prevent multiple reads
  }

  delay(100);
}

bool isAuthorized(String uid) {
  /**
   * Check if UID is in authorized list
   */
  for (int i = 0; i < numAuthorizedUIDs; i++) {
    if (uid == authorizedUIDs[i]) {
      return true;
    }
  }
  return false;
}

void grantAccess(String uid) {
  /**
   * Grant access: unlock door, green LED, success beep
   */
  Serial.println("✅ ACCESS GRANTED");

  // Visual feedback
  digitalWrite(RED_LED, LOW);
  digitalWrite(GREEN_LED, HIGH);

  // Unlock door
  lockServo.write(UNLOCKED_ANGLE);
  isLocked = false;

  // Audio feedback
  beep(50);
  delay(100);
  beep(50);

  Serial.println("🔓 Door unlocked");
  Serial.println("   Relocking in 5 seconds...");

  // Log access
  logAccess(uid, "GRANTED");

  // Keep door unlocked for 5 seconds
  delay(5000);

  // Re-lock door
  lockServo.write(LOCKED_ANGLE);
  isLocked = true;
  digitalWrite(GREEN_LED, LOW);
  digitalWrite(RED_LED, HIGH);

  Serial.println("🔒 Door locked");
}

void denyAccess(String uid) {
  /**
   * Deny access: red LED, error beep
   */
  Serial.println("❌ ACCESS DENIED");
  Serial.println("   Unauthorized NFC tag");

  // Visual feedback (red LED already on)
  // Blink red LED
  for (int i = 0; i < 3; i++) {
    digitalWrite(RED_LED, LOW);
    delay(100);
    digitalWrite(RED_LED, HIGH);
    delay(100);
  }

  // Audio feedback (long error beep)
  beep(500);

  // Log denied attempt
  logAccess(uid, "DENIED");
}

void beep(int duration_ms) {
  /**
   * Sound buzzer for specified duration
   */
  digitalWrite(BUZZER, HIGH);
  delay(duration_ms);
  digitalWrite(BUZZER, LOW);
}

void logAccess(String uid, String status) {
  /**
   * Record access attempt in circular buffer
   */
  accessHistory[accessLogIndex].uid = uid;
  accessHistory[accessLogIndex].status = status;
  accessHistory[accessLogIndex].timestamp = millis();

  accessLogIndex = (accessLogIndex + 1) % 10;  // Circular buffer

  // Print log entry
  Serial.print("📝 Logged: ");
  Serial.print(status);
  Serial.print(" - ");
  Serial.println(uid);
}

// Optional: Add command to print access history via Serial
void printAccessHistory() {
  Serial.println("\n=== Access History (Last 10 attempts) ===");
  for (int i = 0; i < 10; i++) {
    if (accessHistory[i].uid != "") {
      Serial.print(i + 1);
      Serial.print(". ");
      Serial.print(accessHistory[i].status);
      Serial.print(" - ");
      Serial.print(accessHistory[i].uid);
      Serial.print(" @ ");
      Serial.print(accessHistory[i].timestamp / 1000);
      Serial.println("s");
    }
  }
  Serial.println("=====================================\n");
}

Expected Serial Monitor Output:

=== ESP32 NFC Access Control System ===
✓ NFC Reader initialized
📱 Ready to scan NFC tags...

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📱 NFC Tag Detected
   UID: 04 A3 B2 C1 D4 5E 80
✅ ACCESS GRANTED
🔓 Door unlocked
   Relocking in 5 seconds...
📝 Logged: GRANTED - 04 A3 B2 C1 D4 5E 80
🔒 Door locked
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📱 NFC Tag Detected
   UID: FF AA BB CC DD EE 11
❌ ACCESS DENIED
   Unauthorized NFC tag
📝 Logged: DENIED - FF AA BB CC DD EE 11
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

How to Add New Authorized Tags:

  1. Scan an unknown tag to get its UID from Serial Monitor
  2. Add the UID to the authorizedUIDs array:
String authorizedUIDs[] = {
  "04 A3 B2 C1 D4 5E 80",
  "YOUR NEW UID HERE"
};
  1. Update numAuthorizedUIDs count
  2. Re-upload the code

29.5.2 Lab 2: Python NFC Smart Home Automation Server

Create a Raspberry Pi-based NFC smart home control system.

Hardware Required:

  • Raspberry Pi (3B+ or 4)
  • PN532 NFC Module (I2C or UART)
  • NFC tags (Type 2)
  • Optional: Smart home devices (lights, plugs via MQTT)

Installation:

# Install dependencies
pip3 install nfcpy paho-mqtt flask

# Enable I2C on Raspberry Pi
sudo raspi-config
# Interface Options → I2C → Enable

Complete Code:

#!/usr/bin/env python3
"""
NFC Smart Home Automation Server
Raspberry Pi + PN532 + MQTT

Tap NFC tags to trigger smart home scenes
"""

import nfc
import paho.mqtt.client as mqtt
import json
from datetime import datetime
from flask import Flask, jsonify
import threading

class NFCSmartHomeServer:
    def __init__(self):
        # Tag-to-scene mappings
        self.tag_scenes = {}

        # Registered smart home devices
        self.devices = {}

        # Scene definitions
        self.scenes = {}

        # MQTT client
        self.mqtt_client = None

        # Access log
        self.access_log = []

    def setup_mqtt(self, broker="localhost", port=1883):
        """Connect to MQTT broker for smart home control"""
        self.mqtt_client = mqtt.Client(
            mqtt.CallbackAPIVersion.VERSION2,
            client_id="nfc-smart-home"
        )
        self.mqtt_client.connect(broker, port)
        self.mqtt_client.loop_start()
        print(f"✓ Connected to MQTT broker: {broker}")

    def register_device(self, device_id, name, topic):
        """Register a smart home device"""
        self.devices[device_id] = {
            "name": name,
            "topic": topic,
            "state": "OFF"
        }
        print(f"✓ Device registered: {name} ({device_id})")

    def create_scene(self, scene_name, description, actions):
        """Create a smart home scene"""
        self.scenes[scene_name] = {
            "description": description,
            "actions": actions
        }
        print(f"✓ Scene created: {scene_name}")

    def map_tag_to_scene(self, tag_uid, scene_name):
        """Map NFC tag UID to scene"""
        self.tag_scenes[tag_uid] = scene_name
        print(f"✓ Tag {tag_uid} mapped to scene '{scene_name}'")

    def execute_scene(self, scene_name):
        """Execute all actions in a scene"""
        if scene_name not in self.scenes:
            print(f"❌ Unknown scene: {scene_name}")
            return False

        scene = self.scenes[scene_name]
        print(f"\n🎬 Executing scene: {scene_name}")
        print(f"   {scene['description']}")

        for action in scene['actions']:
            self._execute_action(action)

        print(f"✅ Scene '{scene_name}' completed")
        return True

    def _execute_action(self, action):
        """Execute a single action"""
        device_id = action.get('device')
        command = action.get('command')
        value = action.get('value')

        if device_id in self.devices:
            device = self.devices[device_id]

            # Send MQTT command
            if self.mqtt_client:
                payload = json.dumps({
                    "command": command,
                    "value": value
                })
                self.mqtt_client.publish(device['topic'], payload)

            # Update local state
            device['state'] = value
            print(f"✓ {device['name']} {command} {value}")
        else:
            print(f"  → {action.get('description', 'Custom action')}")

    def on_tag_scan(self, tag):
        """Callback when NFC tag is scanned"""
        tag_uid = tag.identifier.hex()

        print("\n" + "=" * 50)
        print("📱 NFC Tag Detected")
        print(f"   UID: {tag_uid}")
        print(f"   Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")

        # Log access
        self.access_log.append({
            "uid": tag_uid,
            "timestamp": datetime.now().isoformat()
        })

        # Find and execute scene
        if tag_uid in self.tag_scenes:
            scene_name = self.tag_scenes[tag_uid]
            self.execute_scene(scene_name)
        else:
            print(f"⚠️  Unknown tag: {tag_uid}")
            print("   Register this tag with a scene")

        print("=" * 50)
        return True

    def start_nfc_listener(self):
        """Start listening for NFC tags"""
        try:
            clf = nfc.ContactlessFrontend('usb')
            print("✓ NFC reader initialized")

            print("\n" + "=" * 50)
            print("🏠 NFC Smart Home Server Running")
            print("=" * 50)
            print("📱 Tap NFC tag to trigger scenes...")

            while True:
                clf.connect(
                    rdwr={'on-connect': self.on_tag_scan},
                    terminate=lambda: False
                )

        except Exception as e:
            print(f"❌ NFC Error: {e}")
            print("   Make sure PN532 is connected")


def main():
    """Main entry point"""
    server = NFCSmartHomeServer()

    # Setup MQTT connection
    server.setup_mqtt(broker="localhost", port=1883)

    # Register smart home devices
    server.register_device("light_001", "Living Room Light", "home/lights/living")
    server.register_device("light_002", "Bedroom Light", "home/lights/bedroom")
    server.register_device("thermo_001", "Thermostat", "home/climate/thermostat")

    # Create scenes
    server.create_scene(
        "Goodnight",
        "Turn off all lights, set thermostat to 68°F",
        [
            {"device": "light_001", "command": "turn", "value": "OFF"},
            {"device": "light_002", "command": "turn", "value": "OFF"},
            {"device": "thermo_001", "command": "set", "value": "68"}
        ]
    )

    server.create_scene(
        "Wake Up",
        "Turn on bedroom light, set thermostat to 72°F",
        [
            {"device": "light_002", "command": "turn", "value": "ON"},
            {"device": "thermo_001", "command": "set", "value": "72"},
            {"description": "Start coffee maker"}
        ]
    )

    server.create_scene(
        "Welcome Home",
        "Turn on lights, adjust temperature",
        [
            {"device": "light_001", "command": "turn", "value": "ON"},
            {"device": "thermo_001", "command": "set", "value": "70"},
            {"description": "Disarm security system"}
        ]
    )

    # Map NFC tags to scenes (replace with your tag UIDs)
    server.map_tag_to_scene("04a3b2c1d45e80", "Goodnight")
    server.map_tag_to_scene("08f7e29a3b1c4d", "Wake Up")
    server.map_tag_to_scene("123456789abcde", "Welcome Home")

    # Start web dashboard in background
    print("✓ Web dashboard: http://localhost:5000")

    # Start NFC listener (blocking)
    server.start_nfc_listener()


if __name__ == "__main__":
    main()

Testing (Without Hardware):

If you don’t have NFC hardware, you can simulate tag scans:

# Add to NFCSmartHomeServer class:
def simulate_tag_scan(self, tag_uid: str):
    """Simulate NFC tag scan for testing"""
    class FakeTag:
        def __init__(self, uid):
            self.identifier = bytes.fromhex(uid)

    self.on_tag_scan(FakeTag(tag_uid))

# Usage:
server.simulate_tag_scan("04a3b2c1d45e80")  # Triggers goodnight scene

Expected Output:

✓ NFC reader initialized
✓ Connected to MQTT broker: localhost
✓ Device registered: Living Room Light (light_001)
✓ Device registered: Bedroom Light (light_002)
✓ Device registered: Thermostat (thermo_001)
✓ Scene created: Goodnight
✓ Scene created: Wake Up
✓ Scene created: Welcome Home
✓ Tag 04a3b2c1d45e80 mapped to scene 'Goodnight'
✓ Tag 08f7e29a3b1c4d mapped to scene 'Wake Up'
✓ Tag 123456789abcde mapped to scene 'Welcome Home'
✓ Web dashboard: http://localhost:5000

==================================================
🏠 NFC Smart Home Server Running
==================================================
📱 Tap NFC tag to trigger scenes...

==================================================
📱 NFC Tag Detected
   UID: 04a3b2c1d45e80
   Time: 2025-01-15 22:30:45

🎬 Executing scene: Goodnight
   Turn off all lights, set thermostat to 68°F
✓ Living Room Light turn OFF
✓ Bedroom Light turn OFF
  → Thermostat set to 68°F
✅ Scene 'Goodnight' completed

==================================================

29.6 Python Implementations

29.6.1 Implementation 1: NFC Tag and Reader Simulator

Expected Output:

=== NFC Tag & Reader Simulation ===

✍️  Writing to tag 04:A3:B2:C1:D4:5E:80
✓ Record written to tag 04:A3:B2:C1:D4:5E:80
✍️  Writing to tag 04:A3:B2:C1:D4:5E:80
✓ Record written to tag 04:A3:B2:C1:D4:5E:80
✍️  Writing to tag 08:F7:E2:9A:3B:1C:4D
❌ Incorrect password for tag 08:F7:E2:9A:3B:1C:4D
✍️  Writing to tag 08:F7:E2:9A:3B:1C:4D
✓ Record written to tag 08:F7:E2:9A:3B:1C:4D

--- Reading Tags ---
📱 Reading tag 04:A3:B2:C1:D4:5E:80 at 2.8cm
  Record 1: U = https://iotclass.example.com
  Record 2: T = Welcome to IoT Class!
❌ Tag out of range: 15.8cm (max: 10.0cm)

🔒 Tag 04:A3:B2:C1:D4:5E:80 is now permanently read-only
✍️  Writing to tag 04:A3:B2:C1:D4:5E:80
❌ Tag 04:A3:B2:C1:D4:5E:80 is locked (read-only)

--- Reader Statistics ---
Total reads: 2
Unique tags: 1
Average distance: 9.3cm

--- Tag Memory ---
Poster tag: 62/48 bytes
Payment tag: 30/4096 bytes

29.6.2 Implementation 2: NFC Payment System

Expected Output:

=== NFC Mobile Payment Simulation ===

✓ Card ending in 4532 added to iPhone-12-ABC123
✓ Card ending in 8765 added to iPhone-12-ABC123

--- Transaction 1: Coffee Purchase ---
💳 Terminal ready: $4.75 at Coffee Shop
📱 Tap your device to pay...
👤 Biometric verified (fingerprint)
✅ Payment successful: $4.75
   Card: ****4532
   Transaction ID: a3f7c2e1d9b4

--- Transaction 2: Grocery Purchase ---
💳 Terminal ready: $47.82 at Coffee Shop
📱 Tap your device to pay...
👤 Biometric verified (fingerprint)
✅ Payment successful: $47.82
   Card: ****4532
   Transaction ID: f8e2a7c3b1d6

--- Transaction 3: Large Purchase (Exceeds Limit) ---
❌ Amount $150.00 exceeds contactless limit $100.00

--- Transaction 4: Without Biometric ---
💳 Terminal ready: $12.50 at Coffee Shop
📱 Tap your device to pay...
⚠️  Biometric not enabled - payment may require PIN
❌ Biometric authentication required

--- Terminal Summary ---
Total transactions: 3
Successful: 2
Today's total: $52.57

29.7 Knowledge Check

Question 1: In the NFC smart home server, why is MQTT used as the communication protocol between the NFC reader and smart home devices?

Question 2: In the ESP32 access control system, why does the code use a circular buffer for access logging instead of a simple array?

When integrating NFC into IoT systems, you must choose between local intelligence (ESP32 reader validates access) or cloud-first (reader only forwards UID, cloud decides).

Local Intelligence Approach (ESP32 Example):

  • ✅ Works offline (no network required)
  • ✅ Fast response (<100 ms decision)
  • ✅ Lower cloud costs (no API call per tap)
  • ❌ Hard to update authorized UIDs remotely
  • ❌ Limited storage (~100-200 UIDs in flash)
  • Best for: Doors, physical access where uptime matters

Cloud-First Approach (Raspberry Pi Example):

  • ✅ Unlimited authorized tags (database-backed)
  • ✅ Real-time updates (add/revoke instantly)
  • ✅ Rich logging and analytics
  • ✅ Multi-site coordination (one central system)
  • ❌ Requires reliable network
  • ❌ ~300-500 ms latency per tap
  • Best for: Smart home automation, attendance systems

Real-World Numbers (50-person office):

Local approach (ESP32 at each door): - Hardware: $10 per door reader - Network: None required - Update time: Must reprogram each ESP32 (15 min/door) - Offline tolerance: 100% (works during network outages)

Cloud approach (Raspberry Pi central server): - Hardware: $45 Raspberry Pi + $12 PN532 per door - Network: Requires Wi-Fi/Ethernet at each door - Update time: Instant (edit database, all doors see change) - Offline tolerance: 0% (doors fail open or fail closed?)

Hybrid Solution (Recommended):

  1. ESP32 caches last 50 authorized UIDs locally
  2. Checks local cache first (<100 ms)
  3. Falls back to cloud API if UID unknown
  4. Syncs cache every 5 minutes
  5. Works offline using cached UIDs

Implementation Pattern:

bool checkAccess(String uid) {
    // 1. Check local cache (fast path)
    if (localCache.contains(uid)) {
        return localCache.isAuthorized(uid);
    }

    // 2. Query cloud API (slow path)
    if (WiFi.status() == WL_CONNECTED) {
        bool authorized = cloudAPI.checkAccess(uid);
        localCache.add(uid, authorized);  // Cache result
        return authorized;
    }

    // 3. Offline fallback
    return false;  // Fail closed (or use cached whitelist)
}

Decision Factors:

  • Internet reliability <95%? → Local intelligence required
  • 100 users? → Cloud-first makes sense

  • Sub-second response critical? → Local cache essential
  • Need audit logs? → Cloud API for every decision

29.8 Concept Relationships

How NFC IoT Components Connect

NFC IoT systems follow a gateway pattern: reader (ESP32 + PN532) → middleware (MQTT broker) → backend (Home Assistant, cloud server). The reader is stateless—it simply publishes UID or NDEF data; business logic lives server-side for easy updates.

ESP32 access control demonstrates local intelligence (UID authorization list in flash), while Raspberry Pi smart home shows cloud-first approach (tag-to-scene mapping in database). Hybrid solutions cache authorization locally but sync with cloud for updates—providing offline resilience with centralized management.

MQTT’s publish/subscribe pattern decouples components: one NFC tap can trigger multiple subscribers (lights, thermostat, security system) without the reader knowing about them. This scales better than point-to-point commands.

29.9 See Also

29.10 Try It Yourself

Build Your Own NFC IoT Gateway

Project: ESP32 NFC Door Log

Modify the access control lab to log all attempts to a MicroSD card: 1. Add SD card module to ESP32 (SPI pins) 2. Log format: timestamp,UID,granted/denied 3. Periodically upload log to cloud via Wi-Fi 4. Detect anomalies (repeated denials = potential attack)

What to learn: Local logging provides audit trail even during network outages.

Common Pitfalls

If the network is unavailable when a tag is scanned, the event is lost unless local buffering is implemented. Fix: store scan events in local persistent storage and upload them in batch when connectivity is restored.

A busy reader scanning 100 tags per minute can exhaust API rate limits or database write capacity. Fix: implement local aggregation and deduplication before forwarding events to the cloud.

NFC UIDs can be cloned by inexpensive tools. Relying on UID alone for access control or authentication is insecure. Fix: use cryptographically authenticated NFC tags (e.g., NTAG 424 DNA with AES-128 challenge-response) for security-sensitive applications.

29.11 Summary

This chapter covered NFC IoT integration:

  • Gateway Pattern: NFC readers connecting to cloud via MQTT publish/subscribe
  • Access Control Lab: ESP32-based door lock with PN532 reader, UID authorization, and circular-buffer logging
  • Smart Home Server: Raspberry Pi automation with scene management, tag-to-action mapping, and MQTT device control
  • MQTT Integration: Publishing NFC scan events to message brokers for decoupled, scalable IoT architectures
  • Local vs Cloud: Decision framework for choosing local intelligence, cloud-first, or hybrid NFC integration patterns

29.12 What’s Next

Chapter Focus
NFC Security and Comparisons Tokenization, SE vs HCE, NFC vs BLE vs QR decision frameworks
RFID Fundamentals RFID tag types, frequency bands, and reader architecture
UWB Communication Ultra-Wideband ranging, cm-level positioning, and IEEE 802.15.4z