1 nfc_test_plan
moggieuk edited this page 2026-07-31 13:27:44 +07:00
This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

NFC/RFID reader validation plan

Companion to nfc_driver_matrix.md. Turns the matrix into something you can run against real hardware. Two tracks, because the failure modes differ:

  • Track S — shared reader: one reader, tags presented by hand. Homing accuracy irrelevant; what matters is Spoolman resolution and that a missing reader degrades gracefully.
  • Track G — per-gate reader: reader per lane, used as a homing endstop. Everything in Track S plus homing behaviour, bus scaling and per-tick cost.

Run B (bench) first regardless. Track G additionally runs H (homing).

Speed note: gear_homing_speed defaults to 150 mm/s (mmu_unit_parameters.py:196), not the 100 mm/s the manager comment uses. Expected overshoot at default speed: ~3 mm (PN532), ~6 mm (RC522), ~15 mm (PN7160 no-IRQ), ~1522 mm (PN5180).


0. Instrumentation (set once)

[mmu_nfc_reader gateN]
debug: 3          # 3 = probe watchdog + parse steps. 4 = full trace (slow, only for a failure)
MMU_TEST_CONFIG log_level=2

Watch: tail -f ~/printer_data/logs/klippy.log | grep -E 'mmu_nfc|rfid|NFC'

Status to sample: printer["mmu_nfc_reader gateN"].alive / .present / .last_uid / .reader_type / .interface

Commands used throughout: MMU_RFID_INIT|READ|RELEASE [NAME=] (driver level), MMU_NFC (manager level), MMU_NFC_SCAN, MMU_PRELOAD.


B. Bench bring-up — run for every reader, before any motion

ID Do Pass A failure means
B1 Klippy restart, read the log No config errors; each reader logs init: gate=N Wiring/bus validation in reader_factory rejected something — the message names the section and the option
B2 MMU_NFC Every reader listed, alive true Chip not answering: power, bus, address, CS
B3 MMU_RFID_READ NAME=x with no tag no tag detected, returns promptly A long hang here is the blocking-read problem the probe exists to avoid; check transceive_delay / pn5180_rf_timeout
B4 MMU_RFID_READ NAME=x with a tag on the antenna UID=..., stable across 10 repeats Intermittent = marginal RF, bus speed, or (software I2C) missing pull-ups
B5 MMU_NFC ... READ=1 DEEP=1 on one tag of each brand you own Log shows deep read uid=... strategy=<ntag_type2/mifare_classic/iso15693_type5> then parsed tag_format=... strategy=uid_only → tag type not classifiable. all MIFARE key attempts failed → wrong vendor or missing pycryptodome (the log says which)
B6 Pull the tag, MMU_RFID_READ, replace, read again Clean no tag then a fresh UID A sticky UID means release/clear is broken
B7 Note the reader's probe capability from the init log RC522/PN532: probe path. PN7160: init OK (tag homing probe available) PN7160 saying unavailable - no irq_pin = you are on the slow shim; expected if you didn't wire IRQ
B8 Chip-specific trap from §X

B7 is the single most important line to read if you intend to home to tags.


S. Shared-reader track

ID Do Pass
S1 Present a tag, wait ≥1 s (poll is NFC_CHECK_INTERVAL = 1.0 s) UID picked up without any command
S2 Leave the tag sitting on the reader 30 s Spoolman is not hammered — one lookup, then the 5 s NFC_TAG_HOLD_TIME cooldown + UID dedupe hold
S3 Swap to a second tag New UID resolves promptly, not blocked by the previous hold
S4 MMU_NFC SHARED=1 REGISTER=1 on an unknown tag with nfc_deep_read=1 + auto-create on Spool created from parsed metadata; report-only for shared (no gate map change)
S5 MMU_NFC SHARED=1 ENABLE=0, present a tag Nothing read at all. ENABLE=1 re-inits
S6 Start a print, present a tag Reader inactive during print (the active flag), no lookups
S7 UART only: unplug the adapter, restart klippy Klippy starts; reader reports not-alive. Replug + MMU_RFID_INIT recovers
S8 UART only: confirm the port is a /dev/serial/by-id/ path No not a /dev/serial/by-id/ path warning in the log

S7 is the shared-reader-specific value claim: a dead reader must not be able to stop the printer from starting.


G. Per-gate track

ID Do Pass
G1 MMU_NFC with every gate loaded Correct UID attributed to the correct gate — no off-by-one, no cross-talk
G2 Move one tag between two gates' readers UID follows the tag; the vacated gate reads empty. Catches a shared-bus/address mix-up that G1 can miss
G3 MMU_NFC GATES=0,1,2,3 READ=1 All gates answer in one command without a stall or a TTC
G4 MMU_NFC GATE=n REGISTER=1 Gate map updated for that gate (per-gate does assign, unlike shared)
G5 Read every gate 20× in a loop Zero read failures. Intermittents here are bus contention, not RF
G6 Software I2C only: same test with all readers configured Any silent cross-bus collision shows as intermittent failures on G5/G2 — reader_factory catches pin/address collisions at config time, but not weak pull-ups
G7 PN7160 multi-drop: 24 readers at 0x280x2B on one bus Each answers at its own address; G2 confirms no aliasing

H. Homing track (per-gate only)

Prereq: gate endstop must be a real MCU switch. If the log says is not a real MCU switch - plain homing or missing endstop ... - plain homing, H-series is inapplicable — the reader is not compounded into the home at all.

ID Do Pass Notes
H1 MMU_NFC_SCAN GATE=n with the tag inside the jog window Tag found, read, filament re-parked at the gate datum Requires nfc_gate_jog_scan_window set (neg,pos)
H2 MMU_NFC_SCAN with the tag already on the reader No motion at all — the pre-read short-circuits Cheapest correctness check of the whole path
H3 MMU_NFC_SCAN with no tag on the spool Sweeps the window, reports not found, re-parks once — filament does not walk backward on repeat invocations Run it 5× and measure the parked position; drift here is the bug the "ONE re-park off a datum" comment exists to prevent
H4 MMU_PRELOAD on a tagged spool Homed and tag read (they are separate outcomes). Ends parked off the gate switch
H5 Repeat H4 20× tag not readable after homing (debug log) appears rarely if ever Frequent = the tag is coasting past the antenna in the deceleration ramp; reduce gear_homing_speed or reposition the reader
H6 Speed sweep — see below Discriminates poll latency from RF geometry The key test
H7 Run H4 in a loop 30× while the extruder heats / a print runs No Timer Too Close Shim drivers (PN5180, PN7160-no-IRQ) are the ones at risk; this is the bench check the endstop's PROTOTYPE header asks for
H8 Immediately after an H4 trigger, MMU_RFID_READ Succeeds Validates the probe→read handoff. RC522 specifically: its probe leaves tags in READY and the read uses REQA, so a broken probe_stop field-drop fails every post-move read
H9 Disable the gate reader, then MMU_PRELOAD Refuses up front with the "reader is disabled" error — does not run the move full length and report no trigger

H6 — the speed sweep

The one measurement that tells you whether your chip choice matters. Run the same homing move at three speeds and record where it triggers:

MMU_TEST_CONFIG UNIT=0 gear_homing_speed=50
MMU_NFC_SCAN GATE=0     # ×5, record the chased distance from the log
MMU_TEST_CONFIG UNIT=0 gear_homing_speed=100
MMU_NFC_SCAN GATE=0     # ×5
MMU_TEST_CONFIG UNIT=0 gear_homing_speed=150
MMU_NFC_SCAN GATE=0     # ×5

Interpretation:

  • Distance grows roughly linearly with speed → poll latency dominates. Implied latency = Δdistance ÷ Δspeed. Compare against §C of the matrix: ~20 ms means the probe path is working; ~100 ms+ means you are on the shim (check B7).
  • Distance roughly constant across speeds → RF coupling range and the deceleration ramp dominate. Your chip choice is not the limiting factor and upgrading from RC522 to PN532 will buy nothing.
  • Spread within one speed exceeds the shift between speeds → tag/antenna geometry is the problem. Fix the mechanical mount before touching driver config.

Record the within-speed spread too; that is your real homing repeatability figure and nothing in the code can improve it.


X. Chip-specific traps

One test each — the thing most likely to be wrong for that chip.

Chip Trap Test
PN532 i2c Only one per bus (0x24 fixed) Configure two on one hardware bus → must be rejected at config time, not produce intermittent reads
PN532 i2c (software) Missing pull-ups; no clock stretching or bus timeout, so a wedged bus returns bad data rather than raising G5 loop ×100. Any garbage UID (not just a miss) points here
PN532 uart Mode pads must be HSU (SEL0=0, SEL1=1); one reader per tty B2 fails → check pads before wiring. Two on one port must be rejected
PN532 spi UNTESTED path. Two specifics to scope: the ACK check compares 6 raw bytes with no status prefix, and every spi_transfer response is indexed [1:] Log must show the UNTESTED against real hardware warning; treat all of B and H as new-code validation, not config validation
PN7160 No irq_pin → silently correct but 5× slower homing B7 log line; then H6 to confirm the latency you actually get
PN7160 Detection tick ends in _wait_for_irq_release(), bounded 50 ms — right at the drip budget H7 (TTC loop) is non-optional for this chip
PN5180 busy_pin + reset_pin are required, no defaults Omit one → config error naming it. Then confirm BUSY actually transitions (a stuck-high BUSY shows as busy_timeout in the log)
PN5180 pn5180_tag_format: auto walks both protocols per read, doubling miss cost Time B3 with auto vs pinned to ntag/iso15693; pin it if you know your tags
RC522 Probe leaves tags in READY; post-move read uses REQA H8 — this fails 100% of the time if the field-drop in probe_stop is broken, and 0% if it works. No ambiguity
RC522 is_alive() reports dead if the TX bits are left clear After any H-series failure, MMU_NFC must still show aliveprobe_stop restores TX bits unconditionally on purpose

Acceptance summary

Setup Must pass Should pass
Shared reader B1B6, S1S6 (+S7S8 if UART) B7 n/a
Per-gate, no homing B1B6, G1G5 (+G6/G7 by topology)
Per-gate, homing to tag all of the above, plus B7, H1H5, H7H9 H6 characterised and recorded

Homing-off-a-tag is not implementable on this code (see matrix §F) — there is nothing to test yet. When it lands, the first test is absence hysteresis: confirm a single missed exchange does not trigger, on a stationary tag, 100 consecutive ticks.