Python NFC Programming Guide

<\/script>\n
'; }, get iframeSnippet() { const domain = '{ SITE_DOMAIN }'; const type = '{ embed_type }'; const slug = '{ embed_slug }'; return ''; }, get activeSnippet() { return this.method === 'script' ? this.scriptSnippet : this.iframeSnippet; }, copySnippet() { navigator.clipboard.writeText(this.activeSnippet).then(() => { this.copied = true; setTimeout(() => { this.copied = false; }, 2000); }); } }" @keydown.escape.window="open = false" @click.outside="open = false">

Embed This Widget

Theme


      
    

Widget powered by . Free, no account required.

nfcpy Library for Desktop Applications

Using the nfcpy library for NFC programming in Python. Covers tag discovery, NDEF operations, card emulation, and LLCP peer-to-peer.

| 4 min read

Python NFC Programming Guide: nfcpy Library

nfcpy by Stephen Tiedemann is the most mature Python library for NFC reader hardware, supporting contactless cards, tag operations, and peer-to-peer sessions on Linux and macOS. It communicates with USB, SPI, and UART-connected NFC reader ICs including the PN532, PN533, RCS380, and ACR122U.

Installation

pip install nfcpy
# Optionally for USB device detection:
pip install libusb1 pyserial

On Linux, grant your user permission to access USB without sudo:

sudo adduser $USER plugdev
# Create udev rule for ACR122U or similar
echo 'SUBSYSTEM=="usb", ATTRS{idVendor}=="072f", MODE="0666", GROUP="plugdev"' \
  | sudo tee /etc/udev/rules.d/93-nfc.rules
sudo udevadm control --reload-rules

Connecting to a Reader

import nfc

def connected(tag):
    print(f"Tag found: {tag}")
    return True  # return False to stop after one tag

with nfc.ContactlessFrontend('usb') as clf:
    clf.connect(rdwr={'on-connect': connected})

nfc.ContactlessFrontend accepts: - 'usb' — auto-detect first USB NFC reader - 'usb:072f:2200' — specific vendor/product IDs - 'tty:S0:pn532' — serial UART device - 'i2c:0' — I2C bus (Raspberry Pi)

The PN532 over I2C is the most common setup for Raspberry Pi projects. See NFC Arduino and Raspberry Pi for wiring.

Reading NDEF from a Tag

import nfc
import ndef

def on_connect(tag):
    if tag.ndef is None:
        print("Tag has no NDEF content")
        return False

    print(f"NDEF version: {tag.ndef.version}")
    print(f"Capacity: {tag.ndef.capacity} bytes")
    print(f"Writeable: {tag.ndef.is_writeable}")

    for record in tag.ndef.records:
        if isinstance(record, ndef.UriRecord):
            print(f"URI: {record.iri}")
        elif isinstance(record, ndef.TextRecord):
            print(f"Text ({record.language}): {record.text}")
        elif isinstance(record, ndef.SmartposterRecord):
            print(f"SmartPoster URI: {record.resource}")
        else:
            print(f"Record type: {record.type!r}")
    return True

with nfc.ContactlessFrontend('usb') as clf:
    clf.connect(rdwr={'on-connect': on_connect})

The tag.ndef attribute is None if the tag has no NDEF capability container or is not NDEF-formatted. All record types map to Python objects via the companion ndeflib package (pip install ndeflib).

Writing NDEF to a Tag

import nfc
import ndef

def write_url(tag):
    if tag.ndef is None:
        print("Cannot write: no NDEF support")
        return False
    if not tag.ndef.is_writeable:
        print("Tag is write-protected — check [lock-bits](/glossary/lock-bits/)")
        return False

    records = [ndef.UriRecord("https://nfcfyi.com")]
    tag.ndef.records = records
    print(f"Written {len(records)} record(s)")
    return False  # stop after write

with nfc.ContactlessFrontend('usb') as clf:
    clf.connect(rdwr={'on-connect': write_url})

Low-Level Tag Access

For operations beyond NDEF — reading raw memory blocks, setting password-protection, or writing otp bytes — access the tag's underlying type object:

def raw_access(tag):
    # Type 2 tag (e.g. NTAG213/215/216)
    if isinstance(tag, nfc.tag.tt2.Type2Tag):
        # Read 4 bytes at page 4 (user memory starts at page 4)
        data = tag.read(4)
        print(f"Page 4: {data.hex()}")

        # Write 4 bytes to page 4
        tag.write(4, bytearray([0x01, 0x02, 0x03, 0x04]))

    # Type 4 tag (e.g. DESFire)
    elif isinstance(tag, nfc.tag.tt4.Type4Tag):
        response = tag.send_apdu(0x00, 0xA4, 0x04, 0x00)
        print(f"APDU response: {response.hex()}")

    return False

NTAG Password Protection

def set_password(tag):
    if not isinstance(tag, nfc.tag.tt2_nxp.NTAG215):
        return False
    # Write 4-byte password to page 85 (0x55)
    tag.write(0x55, bytearray([0x41, 0x42, 0x43, 0x44]))
    # Write 2-byte PACK to page 86
    tag.write(0x56, bytearray([0xAA, 0xBB, 0x00, 0x00]))
    # Set AUTH0 in CFG0 (page 83): protect from page 4 onwards
    cfg0 = bytearray(tag.read(0x53))
    cfg0[3] = 0x04  # AUTH0 = page 4
    tag.write(0x53, cfg0)
    print("Password set")
    return False

Always test password configurations with a test tag before deploying. Incorrect access-control-bits can permanently lock a tag.

Card Emulation and Peer-to-Peer

nfcpy supports card-emulation-mode on readers with hardware support (e.g. RC-S380):

def on_startup(target):
    target.sensf_res = bytearray.fromhex("0112FC..." )
    return target

def on_connect(llc):
    # LLCP logical link control
    print("P2P connected")
    return True

with nfc.ContactlessFrontend('usb') as clf:
    clf.connect(llcp={'on-startup': on_startup, 'on-connect': on_connect})

llcp and snep are used for peer-to-peer data exchange and legacy Android Beam compatibility.

Batch Tag Processing Script

import nfc, ndef, time

tags_processed = 0

def process_tag(tag):
    global tags_processed
    if tag.ndef:
        for record in tag.ndef.records:
            if isinstance(record, ndef.UriRecord):
                print(f"[{tags_processed:04d}] {record.iri}")
    tags_processed += 1
    return True  # keep looping

with nfc.ContactlessFrontend('usb') as clf:
    print("Place tags on reader. Ctrl+C to stop.")
    try:
        clf.connect(rdwr={'on-connect': process_tag, 'interval': 0.1})
    except KeyboardInterrupt:
        print(f"Processed {tags_processed} tags")

The interval parameter (seconds) controls how quickly the reader polls for new tags after the previous one is removed. Use 0.1 s for rapid batch processing.

Supported Reader Hardware

Reader Interface Notes
ACR122U USB Most common; PN532 inside
Identiv uTrust 3700F USB ISO 15693 + 14443
Sony RC-S380 USB P2P / HCE capable
PN532 breakout SPI / I2C / UART Raspberry Pi / Arduino
SCL3711 USB CCID compliant

Use the NFC Reader Modules Compared guide for IC-level selection.

See also: NFC Arduino and Raspberry Pi | ESP32 NFC Development | NDEF Specification Deep Dive | NFC Tag Types Explained

Frequently Asked Questions

Our guides cover a range of experience levels. Getting Started guides are written for beginners with no prior NFC knowledge. Programming guides target developers integrating NFC into mobile apps or embedded systems. Security guides are for engineers designing secure NFC deployments for payments, access control, or authentication.

Most guides require only an NFC-enabled smartphone (iPhone 7+ or any modern Android device) and a few NFC tags (NTAG213 or NTAG215 recommended for beginners, available for under $1 each). Advanced guides may reference USB NFC readers like the ACR122U or Proxmark3 for development and testing.

Yes. Programming guides include code examples for Android (Kotlin/Java with the Android NFC API), iOS (Swift with Core NFC), and web-based tools (Web NFC API for Chrome on Android). All code samples are tested and include inline comments explaining each step.