A Klipper plugin to read and write with a PN532 NFC module.
Write an NFC Tag with the Klipper Web Interface URL. Experimental support for working with OpenPrintTags (COMING SOON!)
-
PN532 NFC Breakout Board
- HiLetGo / Elechouse Generic board: https://amzn.to/49rhoYL (affiliate link)
- Must support SPI mode (most do)
- ~$8-10 USD
-
Raspberry Pi with SPI
- Tested on: Raspberry Pi 4, Pi 3B+
- Should work on: Pi Zero 2W, Pi 5
- SPI interface must be enabled
-
External Power Supply for PN532
- Use external 3.3V or 5V power supply (check your PN532 voltage rating)
- Recommended: Share Pi's 5V rail with proper decoupling capacitor (some boards include this)
- DO NOT power PN532 from Pi's 3.3V pin - causes brownouts
Best Choice: NTAG215
- Black 25mm Adhesive NTAG215 https://amzn.to/4oRBIHr (affiliate link)
- White 25mm Adhesive NTAG215 https://amzn.to/47iB1kn (affiliate link)
- 504 bytes usable memory (plenty for URLs)
- Works with all modern phones (iPhone 7+, Android)
- Standard NFC Forum Type 2 tag
- Cost: ~$0.25 per tag in bulk
Alternative Options:
- NTAG213 - 144 bytes (sufficient for most URLs, cheaper)
- NTAG216 - 888 bytes (overkill for URLs, more expensive)
- MIFARE Ultralight - 48 bytes (too small, not recommended)
- Jumper wires (female-to-female recommended)
The PN532 communicates via SPI, which must be enabled:
sudo raspi-configNavigate: 3 Interface Options → I4 SPI → Yes → OK → Finish
Reboot the Pi:
sudo rebootVerify SPI is enabled:
ls /dev/spidev0.*
# Should show: /dev/spidev0.0 /dev/spidev0.1Install the pn532pi library in Klipper's Python environment:
~/klippy-env/bin/pip install pn532piVerify installation:
~/klippy-env/bin/pip show pn532piCopy the plugin file to Klipper's extras directory:
# If you cloned the repo:
cd /path/to/klippyNFC
cp klippy_nfc.py ~/klipper/klippy/extras/
# Or download directly:
wget https://raw.githubusercontent.com/erikbuild/klippyNFC/main/klippy_nfc.py -O ~/klipper/klippy/extras/klippy_nfc.pySet PN532 to SPI Mode:
Before wiring, configure the PN532 mode switches:
- Most PN532 boards have two small DIP switches (SW1, SW2)
- For SPI mode: SW1=OFF, SW2=ON
- Consult your specific board's documentation if different
Wiring Table:
| PN532 Pin | RPi Pin (Physical) | RPi GPIO | Description |
|---|---|---|---|
| VCC | See warning below | - | Power (3.3V or 5V depending on board) |
| GND | Pin 6 (or any GND) | Ground | Ground |
| SCK | Pin 23 | GPIO 11 | SPI Clock (SCLK) |
| MISO | Pin 21 | GPIO 9 | SPI MISO (Master In, Slave Out) |
| MOSI | Pin 19 | GPIO 10 | SPI MOSI (Master Out, Slave In) |
| SS (NSS) | Pin 24 | GPIO 8 | SPI Chip Select (CE0) |
- DO NOT connect VCC to Pi's 3.3V pin (Pin 1 or 17)
- The PN532 draws too much current and will cause brownouts
- Options for proper power:
- Use external 3.3V/5V power supply with shared ground
- Connect to Pi's 5V pin (Pin 2 or 4) only if your PN532 has a 5V→3.3V regulator
- When in doubt, check your PN532's voltage rating and current requirements
Wiring Diagram:
Raspberry Pi PN532 (SPI Mode)
┌──────────┐ ┌────────────┐
│ Pin 23 │────────▶│ SCK │
│ Pin 21 │◀────────│ MISO │
│ Pin 19 │────────▶│ MOSI │
│ Pin 24 │────────▶│ SS │
│ Pin 6 │────────▶│ GND │
│ Pin 2 │────────▶│ VCC │
└──────────┘ └────────────┘
Add this section to your printer.cfg:
[klippy_nfc]
# SPI Configuration (required)
spi_bus: 0 # SPI bus number: 0 for SPI0 (default), 1 for SPI1
spi_ce: 0 # Chip select: 0 for CE0/GPIO8 (default), 1 for CE1/GPIO7
# URL Configuration (optional)
port: 80 # Web interface port (default: 80 for Mainsail)
# url: http://192.168.1.100:80 # Explicit URL override (auto-detected if not set)Configuration Notes:
spi_busandspi_cedefaults match standard wiring aboveportshould match your Mainsail configuration (usually 80)urlis auto-detected from hostname/IP if not specified- If your network uses mDNS, the auto-detected URL will use
.localdomain
Apply the configuration changes:
sudo systemctl restart klipperCheck for errors:
tail -50 ~/printer_config/logs/klippy.logLook for:
NFC tag writer ready. Use NFC_WRITE_TAG to write tags with URL: http://...- No errors about PN532 initialization
If you see Failed to initialize PN532, check wiring and power supply.
Step-by-step:
-
Prepare a blank NFC tag (NTAG215 recommended)
-
Place tag on PN532 reader
- Position tag flat against the PN532 antenna
- Tag should be centered over the chip
- Keep phone away during writing to avoid interference
-
Open your Klipper web interface (Mainsail/Fluidd)
- Navigate to the G-code console
-
Run the write command:
NFC_WRITE_TAG
-
Wait for completion
- You should see:
Scanning for NFC tag... Tag detected: 04a1b2c3d4e5f6 Writing URL: http://printer.local:7125 Success! Successfully wrote 48 bytes (12 pages)
- You should see:
-
Remove tag
- Your tag is now programmed!
- Test by tapping your phone to it
Write the current URL to an NFC tag.
Basic usage:
NFC_WRITE_TAGWith custom URL:
NFC_WRITE_TAG URL=http://192.168.1.100:7125With custom port:
NFC_WRITE_TAG URL=http://ender3.local:80Examples:
# Write tag for Mainsail on default port
NFC_WRITE_TAG
# Write tag for specific IP and port
NFC_WRITE_TAG URL=http://10.0.1.50:7125
# Write tag for Fluidd
NFC_WRITE_TAG URL=http://fluidd.local:7125
# Write tag for custom port
NFC_WRITE_TAG URL=http://prusa-mk4.local:8080Output:
Scanning for NFC tag...
Tag detected: 04123456789abc
Writing URL: http://printer.local:7125
Success! Successfully wrote 48 bytes (12 pages)
Errors:
Error: No NFC tag detected. Place tag on reader and try again.
Error: Failed to write page 5
Display current tag writer status and configuration.
Usage:
NFC_STATUSOutput when never written:
Current URL: http://printer.local:7125
Last write: Not written yet
Output after successful write:
Current URL: http://printer.local:7125
Last write: Successfully wrote 48 bytes (12 pages)
Write time: 2025-01-15 14:23:45
Output after failed write:
Current URL: http://printer.local:7125
Last write: Failed: No tag detected
Write time: 2025-01-15 14:20:12
Change the URL that will be written to future tags.
Usage:
NFC_SET_URL URL=http://new-url.local:7125Examples:
# Change to specific IP
NFC_SET_URL URL=http://192.168.1.100:7125
# Change to custom domain
NFC_SET_URL URL=http://my-printer.home:7125
# Change port
NFC_SET_URL URL=http://printer.local:80Output:
NFC URL updated to: http://192.168.1.100:7125
Notes:
- This only affects future tag writes
- Already-written tags are not affected
- Change persists until Klipper restart (reverts to config/auto-detected URL)
- Use this for writing multiple tags with different URLs
Read and display full tag details.
Usage:
NFC_READ_TAGOutput:
=== Tag Details ===
UID: 046da0cd2e6180
URL: http://printer.local:80
Tag Version: 1.0
Memory Size: 496 bytes
NDEF Length: 26 bytes
NDEF Data: d10116550374686f...
Verify that a tag contains the expected URL.
Usage:
NFC_VERIFY_TAG # Verify against current URL
NFC_VERIFY_TAG URL=http://test.local:80 # Verify against specific URLOutput:
SUCCESS: Tag URL matches expected URL
Or if mismatch:
MISMATCH: Tag contains different URL
Expected: http://printer.local:80
Actual: http://oldprinter.local:7125
Display tag hardware information.
Usage:
NFC_TAG_INFOOutput:
=== Tag Information ===
UID: 046da0cd2e6180
Tag Type: NTAG or MIFARE Ultralight (7-byte UID)
Format: NDEF (Type 2 Tag)
Memory Capacity: 496 bytes
Read Access: Allowed
Write Access: Allowed
Physical NFC tags written by this plugin are standard NDEF format and work with:
-
✅ iPhone 7 and later (iOS 14+ required for automatic reading)
- Works in background - no app needed
- Notification appears when tag is detected
-
✅ Android phones with NFC (Android 4.0+)
- NFC must be enabled in Settings
- Opens browser automatically
Symptom: Klipper log shows Failed to initialize PN532, NFC tag writing disabled
Causes and fixes:
-
Wiring problems
- Double-check all 6 connections (VCC, GND, SCK, MISO, MOSI, SS)
- Ensure wires are firmly seated
- Try different jumper wires (they can be faulty)
- Check for cold solder joints if you soldered connections
-
SPI not enabled
- Verify:
ls /dev/spidev0.*should show devices - Re-run:
sudo raspi-config→ Interface Options → SPI → Enable - Reboot after enabling
- Verify:
-
Wrong SPI mode
- Check PN532 mode switches: SW1=OFF, SW2=ON for SPI
- Power cycle PN532 after changing switches
- Some boards use jumpers instead of switches - check your board's docs
-
Power supply insufficient
- PN532 needs stable 3.3V or 5V (check your board's specs)
- DO NOT use Pi's 3.3V pin (Pin 1/17) - insufficient current
- Use external power supply or Pi's 5V pin (if your board has regulator)
- Add 10μF capacitor between VCC and GND for stability
-
Wrong chip select pin
- Default uses CE0 (GPIO 8, Pin 24)
- If using CE1: change
spi_ce: 1in config - Verify wiring matches config
Symptom: Import error in Klipper logs
Fix:
# Install in Klipper's Python environment
~/klippy-env/bin/pip install pn532pi
# Verify installation
~/klippy-env/bin/pip show pn532pi
# If still not found, try reinstalling
~/klippy-env/bin/pip uninstall pn532pi
~/klippy-env/bin/pip install pn532piVerify PN532 works outside of Klipper:
# Activate Klipper's Python environment
source ~/klippy-env/bin/activate
# Test Python script
python3 << 'EOF'
from pn532pi import Pn532, Pn532Spi
spi = Pn532Spi(0) # CE0
nfc = Pn532(spi)
nfc.begin()
version = nfc.getFirmwareVersion()
if version:
print(f"PN532 firmware version: {version:#x}")
print("PN532 is working!")
else:
print("Failed to communicate with PN532")
EOFIf this fails, it's a hardware/wiring issue, not a Klipper issue.
| Error Message | Meaning | Fix |
|---|---|---|
Failed to communicate with PN532 |
No response from PN532 | Check wiring, power, SPI mode |
pn532pi library not found |
Python dependency missing | Install pn532pi |
No NFC tag detected |
Tag not in range or incompatible | Check tag type, placement |
Failed to write page X |
Write operation failed | Check tag protection, memory |
Chip select must be 1 or 0 |
Invalid config value | Fix spi_ce in printer.cfg |
The plugin automatically discovers your web interface URL:
- Hostname lookup:
socket.gethostname() - IP fallback: If hostname is "localhost", connects to 8.8.8.8 to determine local IP
- Port configuration: Uses
portparameter (default 7125) - Manual override:
urlparameter takes precedence if set
Example auto-detected URLs:
http://mainsailos.local:80(hostname with mDNS)http://192.168.1.100:80(IP address)http://prusawire.local:80(custom hostname)
Planned Features:
- Tag reading capability (verify written URL)
- OpenPrintTag!
MIT License - see LICENSE file
Contributions welcome! Please test thoroughly with your hardware before submitting PRs.
- Built on pn532pi by gassajor000