Skip to content

Hardware

My PN532 board is wired up and completely silent

Check the bus selector before the wiring. The board must be told whether it is speaking UART, I²C or SPI, and the default is often not the one you assumed.

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.

00preamble00 FFstart code02LEN2 bytesFELCScheckD4TFIto chip02CCversion2ADCScheck00postamble
GetFirmwareVersion, whole. The user manual prints this exact byte string as its own worked example of a frame. LEN counts the frame identifier plus the payload, which is why two bytes of content give a length of 02; the two checksums are chosen so each pair sums to zero in the low byte. The frame identifier is D4 from the host and D5 coming back, so you can always tell which direction a captured frame was traveling.

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.

HostPN532Command frameD4 02Acknowledged00 00 FF 00 FF 00Response frameD5 03 32 ...Optional acknowledgement00 00 FF 00 FF 00
One command, up to four frames. The acknowledgment is a fixed six-byte pattern and carries no payload, so it is easy to recognize once you know to expect it. Its absence is information too. When the chip detects a bad length or data checksum, or a framing or timeout error on the serial link, it simply does not acknowledge. So silence after a command usually means the frame you built was malformed, not that the command is unsupported.

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.

D5TFIfrom chip4Banswer4B01NbTg1 found01Tglogical 104 00ATQASENS_RES08SAKSEL_RES04len4 bytes92 2E 58 32NFCID1 · the UID
The answer you are working toward, taken from the user manual's own worked example of a single type A target. The manual calls the two highlighted fields SENS_RES and SEL_RES; ISO 14443 and every tool you will use call the same two bytes ATQA and SAK. Seeing these on screen is the moment the radio is proved as well as the link. The card was found, singled out, and told you what protocol it speaks.

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.

Bus selectorUART, I2C or SPIWiring3.3 V, common groundFirmware versionproves the host linkNormal modeSAMConfigurationPoll for a cardATQA, SAK, UID
Five steps, and the value is in doing them in this order. Each one narrows what can still be wrong, so a failure at any step has a small and known set of causes. Starting at the last step, putting a card on an unproven board, leaves every cause open at once. That is why a silent board feels so much harder to debug than it is.

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.

Where to go next

Read nextHow do I send my very first APDU to a payment card?