PN532 breakout boards carry a pair of DIP switches or solder jumpers that select the host interface. The chip does not autodetect. If the selector says I²C and you have wired a serial adapter to the TX and RX pins, everything is connected correctly and nothing will ever answer, which reads exactly like a dead board.
The user manual is unusually direct about this. Only one link can be used at a time, and the choice is made by a hardware configuration on two interface-mode lines, I0 and I1, read during the chip's power-up sequence. Two consequences follow that catch people. The selection is sampled at power-up, so flipping a switch on a running board changes nothing until you cut the power. And because it is hardware, no amount of host-side configuration can override a wrong setting.
- Interface
- HSU (high-speed UART)
- I0
- 0
- I1
- 0
- What the chip then expects
- 8 data bits, no parity, 1 stop bit, 115200 baud, LSB first.
- Interface
- I²C
- I0
- 1
- I1
- 0
- What the chip then expects
- Slave address 0x48, clock up to 400 kHz, MSB first, and pull-ups you must add yourself.
- Interface
- SPI
- I0
- 0
- I1
- 1
- What the chip then expects
- Mode 0, data sampled on the first clock edge, SCK active high, LSB first.
- Interface
- Reserved
- I0
- 1
- I1
- 1
- What the chip then expects
- Nothing. This combination is marked RFU, so a board left in it is silent by specification.
Two electrical details cause more silent boards than the selector does, and both are in the vendor guides rather than in anyone's memory. The PN532 is a 3.3 V part, so a 5 V microcontroller needs a level shifter between them, and Adafruit's own wiring walkthrough is built around one. And on I²C the breakout does not include the bus pull-up resistors, so you add them yourself: Adafruit's guide specifies 1.5 kΩ from each of SCL and SDA to 3.3 V.
Ask the chip who it is before you show it a card#
Once the selector matches the wiring, do not start with a card. Ask the chip to identify itself first, because that separates "my host link is wrong" from "my antenna or card is wrong" in one step. GetFirmwareVersion is command code 0x02 in the PN532 command set, takes no arguments, and answers with the chip identifier and version.
Everything the host sends travels inside the same envelope, and you should see it once even if your library builds it for you. A normal information frame starts with a preamble, the two-byte start code 00 FF, a length, and a length checksum chosen so that length and checksum sum to zero in the low byte. Then comes a frame identifier saying which way the message is going, the payload, a data checksum obeying the same rule, and a postamble.
The answer comes back as D5 03 followed by four bytes: an identifier for the chip, the firmware version, the firmware revision, and a byte of supported functionality. Only the first of those needs memorizing. For a PN532 the identifier byte is 0x32, and seeing it is the moment your host link is proved. Everything after that is a question about the radio, not about the wiring.
One more thing in that exchange matters if you write your own host code: the chip acknowledges before it answers. The host is always the master. It sends a command, the chip sends back a short acknowledgment frame to say the command arrived intact, the chip then executes it, and only then does the response come. A host that reads one frame and treats it as the answer will read the acknowledgment and conclude the chip is talking nonsense.
Then normal mode, then polling#
SAMConfiguration is the step people leave out, and leaving it out produces the most confusing symptom in this guide. The board answers version queries perfectly and then ignores every card you put on it. The command selects the internal data path. Mode 0x01 is normal mode, in which no security module is involved and the chip acts as an ordinary reader. The other three modes route the radio through a companion chip that you almost certainly do not have.
- Command
- GetFirmwareVersion
- Code
- 0x02
- Use it to
- Prove the host link works at all. Always the first command.
- Command
- GetGeneralStatus
- Code
- 0x04
- Use it to
- Ask the chip what it currently sees, including whether an external field is present.
- Command
- SAMConfiguration
- Code
- 0x14
- Use it to
- Put the chip in normal mode. Skipping it is why a board answers version queries and then ignores cards.
- Command
- InListPassiveTarget
- Code
- 0x4A
- Use it to
- Find a card in the field and get its identifier.
- Command
- InDataExchange
- Code
- 0x40
- Use it to
- Send an APDU to the card you just found. This is the one that carries EMV.
- Command
- InCommunicateThru
- Code
- 0x42
- Use it to
- Send raw frames without the chip managing chaining and error handling for you.
Now poll. InListPassiveTarget takes two arguments: how many targets you will accept, which must not exceed two because the chip handles at most two at once, and a byte naming the radio flavor to look for. For a payment card that byte is 0x00, meaning 106 kbps type A. The other values cover FeliCa polling at 212 and 424 kbps, type B at 106 kbps, and the Innovision Jewel tag. If type A finds nothing and you suspect the card is not type A, type B is the one to try next.
Have two readings of that response ready. If the number-of-targets byte is zero, the command completed and found nothing within the retries it was allowed. That is a normal answer, not an error, and it points at distance, orientation or the wrong radio flavor rather than at your code. And the SAK byte is not decoration. The chip reads it to decide whether the card speaks the ISO 14443-4 transport protocol, and by default it will then send the protocol's activation command by itself. So a payment card is usually ready for APDUs by the time this response reaches you.
With a target listed you have a logical target number, and that number is the first argument to InDataExchange, the command that carries an APDU to the card and brings its answer back. That is where this guide hands over. The next question is what to put inside it, and the answer to that is the same on every EMV card on earth.