You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Arduino新手求助:PN532无法验证NFC卡块,未知验证块

嘿,我刚入门Arduino玩NFC的时候也踩过一模一样的坑!让我来帮你一步步解决~

解决PN532库authenticateBlock()卡壳及块验证信息缺失问题

一、先明确你的两个核心痛点

  • authenticateBlock()卡壳:MIFARE卡的验证依赖密钥和块权限,新手容易在密钥匹配、块选择上出错
  • 看不到验证的块号:默认示例代码没加调试日志,咱们自己补就行

二、一步步排查解决

1. 先补上块验证的日志,明确卡在哪一步

你现在的输出看不到具体验证哪个块,咱们给代码加个打印,把要验证的块号明确输出。找到调用authenticateBlock()的地方,改成这样:

// 你示例里要写的是块0x08,先定义块号变量
uint8_t targetBlock = 0x08;
// 打印当前要验证的块号
Serial.print("正在尝试验证块: 0x");
Serial.println(targetBlock, HEX);

// 调用验证函数,同时保存结果
bool authResult = nfc.authenticateBlock(
    nfc.uid, 
    nfc.uidLength, 
    targetBlock, 
    0, // 0代表用A密钥,1代表用B密钥
    (uint8_t[]) {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} // 通用默认密钥
);

// 打印验证结果
if (!authResult) {
    Serial.print("块0x");
    Serial.print(targetBlock, HEX);
    Serial.println(" 验证失败!");
    return;
}

这样你就能在串口输出里直接看到是哪个块验证失败,不用再瞎猜了。

2. 解决authenticateBlock()失败的核心原因

MIFARE 1K卡的验证逻辑有几个新手容易踩的坑,逐个排查:

  • 默认密钥不一定通用:虽然大部分新卡用0xFF 0xFF 0xFF 0xFF 0xFF 0xFF,但有些厂商的卡会用0xA0 0xA1 0xA2 0xA3 0xA4 0xA5,两种都试试
  • 别碰只读/控制块:你代码注释里提到块0是只读的,绝对不能写!另外每个扇区的最后一个块(比如0x03、0x07、0x0B)是扇区控制块,存密钥和权限,写错了卡直接报废,新手就老老实实写0x08、0x09这类普通数据块
  • 密钥类型搞反了:authenticateBlock的第4个参数是密钥类型,0是A密钥,1是B密钥,两种都尝试一遍
  • UID读取是否正确:从你的输出看Read card #3122678656是UID的十进制,你可以转成十六进制和卡的物理UID对比,避免UID传递错误

3. 调试版完整代码参考

把上面的修改整合到代码里,你可以直接用这个版本调试:

#include <PN532.h>
#include <PN532_SPI.h>

// 根据你的接线选择通信方式,这里用SPI示例
PN532_SPI pn532spi(SPI, 10);
PN532 nfc(pn532spi);

void setup(void) {
  Serial.begin(115200);
  while (!Serial) {} // 等待串口连接

  nfc.begin();
  uint32_t version = nfc.getFirmwareVersion();
  if (!version) {
    Serial.println("找不到PN532模块!检查接线");
    while (1);
  }
  Serial.print("PN532固件版本: ");
  Serial.print((version >> 24) & 0xFF, HEX);
  Serial.print('.');
  Serial.println((version >> 16) & 0xFF, HEX);

  nfc.setPassiveActivationRetries(0xFF);
  nfc.SAMConfig();
}

void loop(void) {
  uint8_t success;
  uint8_t uid[7] = {0};
  uint8_t uidLen;

  Serial.println("\n等待NFC卡靠近...");
  success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLen, 2000);

  if (success) {
    Serial.println("找到1张卡");
    Serial.print("Sens Response: 0x");
    Serial.println(nfc.getSensResp(), HEX);
    Serial.print("Sel Response: ");
    for (uint8_t i=0; i<uidLen; i++) {
      Serial.print("0x");
      Serial.print(uid[i], HEX);
      Serial.print(" ");
    }
    Serial.println();
    uint32_t cardId = ((uint32_t)uid[0]<<24)|((uint32_t)uid[1]<<16)|((uint32_t)uid[2]<<8)|uid[3];
    Serial.print("读取卡ID: #");
    Serial.println(cardId);

    // 要写入的目标块(选普通数据块0x08)
    uint8_t targetBlock = 0x08;
    // 准备16字节的写入数据(MIFARE块固定大小)
    uint8_t writeData[] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,
                           0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10};

    // 第一步:验证块
    Serial.print("正在尝试验证块: 0x");
    Serial.println(targetBlock, HEX);
    bool authOk = nfc.authenticateBlock(uid, uidLen, targetBlock, 0, (uint8_t[]) {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF});
    if (!authOk) {
      Serial.println("A密钥验证失败,尝试B密钥...");
      authOk = nfc.authenticateBlock(uid, uidLen, targetBlock, 1, (uint8_t[]) {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF});
      if (!authOk) {
        Serial.println("B密钥也失败!试试默认密钥0xA0A1A2A3A4A5");
        return;
      }
    }

    // 第二步:写入数据
    Serial.println("验证成功,开始写入...");
    success = nfc.writeMemoryBlock(targetBlock, writeData);
    if (success) {
      Serial.print("块0x");
      Serial.print(targetBlock, HEX);
      Serial.println(" 写入成功!");
      // 读取回来验证
      uint8_t readBack[16];
      success = nfc.readMemoryBlock(targetBlock, readBack);
      if (success) {
        Serial.print("读取写入的数据: ");
        for (uint8_t i=0; i<16; i++) {
          Serial.print("0x");
          Serial.print(readBack[i], HEX);
          Serial.print(" ");
        }
        Serial.println();
      }
    } else {
      Serial.println("写入失败!检查块是否为只读或权限设置");
    }

    nfc.stopCrypto(); // 停止加密
  }
  delay(1000);
}

三、新手额外注意事项

  • 串口波特率要匹配:确保串口监视器的波特率和代码里的Serial.begin(115200)一致
  • 接线要牢固:PN532模块的通信引脚(SPI/I2C)接触不良会导致各种奇怪问题
  • 别乱改控制块:每个扇区的最后一个块是控制块,用来存密钥和权限,新手绝对别碰,写错直接锁卡

内容的提问来源于stack exchange,提问作者user8468734

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:40:47