【问题标题】:PN532 emulated card not read by an Android phonePN532 模拟卡无法被 Android 手机读取
【发布时间】:2020-04-29 07:51:44
【问题描述】:

我正在使用Arduino UNOelechouse's library 以及通过SPI 连接的PN532 模块。

我正在尝试使用该库中的 emulate_tag_ndef 示例模拟卡片,但是当我尝试使用 Samsung Galaxy S7 上的 NFC 工具应用程序读取模拟卡片时,我得到一个空序列号,我不要收到类似于thisNdef 消息。

当我尝试根据Github上的链接问题下方的帖子更改库中的命令数组时,我的手机根本无法检测到模拟卡。

PN532 适用于所有其他NFC 模式(读/写、点对点)对我来说没问题。

【问题讨论】:

    标签: android arduino nfc pn532


    【解决方案1】:

    好吧,我尝试了很多解决方案,我可以勇敢地声称,Android 仅适用于 PN532 的 HCE 模式,而 Ios 仅适用于 NDEF。

    【讨论】:

    【解决方案2】:

    只需像我一样取消注释一些东西,它应该可以工作。

    void setup()
    {
    Serial.begin(115200);
    Serial.println("------- Emulate Tag --------");
    
    message = NdefMessage();
    message.addUriRecord("http://www.elechouse.com");
    messageSize = message.getEncodedSize();
    if (messageSize > sizeof(ndefBuf)) {
      Serial.println("ndefBuf is too small");
      while (1) { }
    }
    
    Serial.print("Ndef encoded message size: ");
    Serial.println(messageSize);
    
    message.encode(ndefBuf);
    
    // comment out this command for no ndef message
    nfc.setNdefFile(ndefBuf, messageSize);
    
    // uid must be 3 bytes!
    nfc.setUid(uid);
    
    nfc.init();
    }
    
    void loop(){
    // uncomment for overriding ndef in case a write to this tag occured
    nfc.setNdefFile(ndefBuf, messageSize); 
    
    // start emulation (blocks)
    nfc.emulate();
    
    // or start emulation with timeout
    if(!nfc.emulate(1000)){ // timeout 1 second
      Serial.println("timed out");
    }
    
    // deny writing to the tag
    // nfc.setTagWriteable(false);
    
    if(nfc.writeOccured()){
       Serial.println("\nWrite occured !");
       uint8_t* tag_buf;
       uint16_t length;
    
       nfc.getContent(&tag_buf, &length);
       NdefMessage msg = NdefMessage(tag_buf, length);
       msg.print();
    }
    

    【讨论】:

      【解决方案3】:

      在 Seeeds 工作室查看此问题:

      https://github.com/Seeed-Studio/PN532/issues/88

      Android 手机仅接受 Felica 卡。如果你放一些随机的ID,它不会被检测为NDEF卡。

      要正确模拟它,您必须以这种方式设置卡 ID:

      uint8_t command[] = {
        PN532_COMMAND_TGINITASTARGET,
        0x05,                  // MODE: 0x04 = PICC only, 0x01 = Passive only (0x02 = DEP only)
      
        // MIFARE PARAMS
        0x04, 0x00,         // SENS_RES (seeeds studio set it to 0x04, nxp to 0x08)
        0x00, 0x00, 0x00,   // NFCID1t    (is set over sketch with setUID())
        0x20,               // SEL_RES    (0x20=Mifare DelFire, 0x60=custom)
      
        // FELICA PARAMS
        0x01, 0xFE,         // NFCID2t (8 bytes) https://github.com/adafruit/Adafruit-PN532/blob/master/Adafruit_PN532.cpp FeliCa NEEDS TO BEGIN WITH 0x01 0xFE!
        0x05, 0x01, 0x86,
        0x04, 0x02, 0x02,
        0x03, 0x00,         // PAD (8 bytes)
        0x4B, 0x02, 0x4F, 
        0x49, 0x8A, 0x00,   
        0xFF, 0xFF,         // System code (2 bytes)
      
        0x01, 0x01, 0x66,   // NFCID3t (10 bytes)
        0x6D, 0x01, 0x01, 0x10,
        0x02, 0x00, 0x00,
      
        0x00, // length of general bytes
        0x00  // length of historical bytes
      };
      

      NFCID1t 是卡的 UID,你可以在你的草图上设置一些东西。
      NFCID2t 必须完全是我在上面的代码中编写的 => Felica 卡
      NFCID3t 可以是一些随机数。

      您会看到您的草图还会出现其他问题,有时无法调试或查看它为什么不起作用(例如,如果您有多个 NDEF 标签)。 因此,测试您的 PN532 最重要的工具是 NXP 的这个工具:
      https://play.google.com/store/apps/details?id=com.nxp.taginfolite

      这会给你很多信息。即使格式不正确,此应用也可以读取您的卡片。

      编辑:这就是我从PN532 library 更新emulatetag.cpp 的方式:

      bool EmulateTag::emulate(const uint16_t tgInitAsTargetTimeout){
      
        // https://www.nxp.com/docs/en/nxp/application-notes/AN133910.pdf
          // Doc: 
          //     Mode: 0x00 any command is accepted. 0x02 only ATR_REQ. 0x04 only RATS (ISO1443-4)
          //     Mifare: SENS_RES => bit 6 and 7 must be 0!
          //             NFCID1t  => first byte must be 0x08 according to the ISO
          //             SEL_RES  => bit 6 must be 1, to enable NFC protocol (example 0x40)
          //     FeliCa: NFCID2t => first 2 bytes must be 0x01 and 0xFE
          //      
        uint8_t command[] = {
            PN532_COMMAND_TGINITASTARGET,
            0x05,                  // MODE: 0x04 = PICC only, 0x01 = Passive only (0x02 = DEP only)
      
            // MIFARE PARAMS
            0x04, 0x00,         // SENS_RES (seeeds studio set it to 0x04, nxp to 0x08)
            0x00, 0x00, 0x00,   // NFCID1t    (is set over sketch with setUID())
            0x20,               // SEL_RES    (0x20=Mifare DelFire, 0x60=custom)
      
            // FELICA PARAMS
            0x01, 0xFE,         // NFCID2t (8 bytes) https://github.com/adafruit/Adafruit-PN532/blob/master/Adafruit_PN532.cpp FeliCa NEEDS TO BEGIN WITH 0x01 0xFE!
            0x05, 0x01, 0x86,
            0x04, 0x02, 0x02,
            0x03, 0x00,         // PAD (8 bytes)
            0x4B, 0x02, 0x4F, 
            0x49, 0x8A, 0x00,   
            0xFF, 0xFF,         // System code (2 bytes)
      
            0x01, 0x01, 0x66,   // NFCID3t (10 bytes)
            0x6D, 0x01, 0x01, 0x10,
            0x02, 0x00, 0x00,
      
            0x00, // length of general bytes
            0x00  // length of historical bytes
        };
      
        if(uidPtr != 0){  // if uid is set copy 3 bytes to nfcid1
          memcpy(command + 4, uidPtr, 3);
        }
      
        switch(pn532.tgInitAsTarget(command,sizeof(command), tgInitAsTargetTimeout))
        {
            case 1: break;
            case 0: DMSG("tgInitAsTarget timed out!"); return false; break;
            case -2: DMSG("tgInitAsTarget failed!"); return false;  break;
        }
      
        uint8_t compatibility_container[] = {
          0, 0x0F,
          0x20,
          0, 0x54,
          0, 0xFF,
          0x04,       // T
          0x06,       // L
          0xE1, 0x04, // File identifier
          ((NDEF_MAX_LENGTH & 0xFF00) >> 8), (NDEF_MAX_LENGTH & 0xFF), // maximum NDEF file size
          0x00,       // read access 0x0 = granted
          0x00        // write access 0x0 = granted | 0xFF = deny
        };
      
        if(tagWriteable == false){
          compatibility_container[14] = 0xFF;
        }
      
        tagWrittenByInitiator = false;
      
        uint8_t rwbuf[128];
        uint8_t sendlen;
        int16_t status;
        int16_t totalReads = 0;
        tag_file currentFile = NONE;
        uint16_t cc_size = sizeof(compatibility_container);
        bool runLoop = true;
      
        while(runLoop){
          status = pn532.tgGetData(rwbuf, sizeof(rwbuf));
          if(status < 0){
            if (status == -2)
            {
              if (totalReads == 0)
              {
                if (pn532.tgInitAsTarget(command, sizeof(command), 1000) == 1) continue;
              }
              else
              {
                DMSG("Transmission over.\n");
                pn532.inRelease();
                return true;
              }
            }
            DMSG("tgGetData failed!\n");
            pn532.inRelease();
            return false;
          }
          totalReads++;
      
          uint8_t p1 = rwbuf[C_APDU_P1];
          uint8_t p2 = rwbuf[C_APDU_P2];
          uint8_t lc = rwbuf[C_APDU_LC];
          uint16_t p1p2_length = ((int16_t) p1 << 8) + p2;
      
          switch(rwbuf[C_APDU_INS]){
            case ISO7816_SELECT_FILE:
              switch(p1){
                case C_APDU_P1_SELECT_BY_ID:
                  if(p2 != 0x0c){
                    DMSG("C_APDU_P2 != 0x0c\n");
                    setResponse(COMMAND_COMPLETE, rwbuf, &sendlen);
                  } else if(lc == 2 && rwbuf[C_APDU_DATA] == 0xE1 && (rwbuf[C_APDU_DATA+1] == 0x03 || rwbuf[C_APDU_DATA+1] == 0x04)){
                    setResponse(COMMAND_COMPLETE, rwbuf, &sendlen);
                    if(rwbuf[C_APDU_DATA+1] == 0x03){
                      currentFile = CC;
                    } else if(rwbuf[C_APDU_DATA+1] == 0x04){
                      currentFile = NDEF;
                    }
                  } else {
                  setResponse(TAG_NOT_FOUND, rwbuf, &sendlen);
                  }
                  break;
                case C_APDU_P1_SELECT_BY_NAME: 
                  const uint8_t ndef_tag_application_name_v2[] = {0, 0x07, 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01 };
                  if(0 == memcmp(ndef_tag_application_name_v2, rwbuf + C_APDU_P2, sizeof(ndef_tag_application_name_v2))){
                    setResponse(COMMAND_COMPLETE, rwbuf, &sendlen);
                  } else{
                    DMSG("function not supported\n");
                    setResponse(FUNCTION_NOT_SUPPORTED, rwbuf, &sendlen);
                  } 
                  break;
              }
              break;
            case ISO7816_READ_BINARY:
              switch(currentFile){
                case NONE:
                  setResponse(TAG_NOT_FOUND, rwbuf, &sendlen);
                  break;
                case CC:
                  if( p1p2_length > NDEF_MAX_LENGTH){
                    setResponse(END_OF_FILE_BEFORE_REACHED_LE_BYTES, rwbuf, &sendlen);
                  }else {
                    memcpy(rwbuf,compatibility_container + p1p2_length, lc);
                    setResponse(COMMAND_COMPLETE, rwbuf + lc, &sendlen, lc);
                  }
                  break;
                case NDEF:
                  if( p1p2_length > NDEF_MAX_LENGTH){
                    setResponse(END_OF_FILE_BEFORE_REACHED_LE_BYTES, rwbuf, &sendlen);
                  }else {
                    memcpy(rwbuf, ndef_file + p1p2_length, lc);
                    setResponse(COMMAND_COMPLETE, rwbuf + lc, &sendlen, lc);
                  }
                  break;
              }
              break;    
            case ISO7816_UPDATE_BINARY:
              if(!tagWriteable){
                setResponse(FUNCTION_NOT_SUPPORTED, rwbuf, &sendlen);
              } else{      
                if( p1p2_length > NDEF_MAX_LENGTH){
                  setResponse(MEMORY_FAILURE, rwbuf, &sendlen);
                }
                else{
                  memcpy(ndef_file + p1p2_length, rwbuf + C_APDU_DATA, lc);
                  setResponse(COMMAND_COMPLETE, rwbuf, &sendlen);
                  tagWrittenByInitiator = true;      
                  uint16_t ndef_length = (ndef_file[0] << 8) + ndef_file[1];
                  if ((ndef_length > 0) && (updateNdefCallback != 0)) {
                    updateNdefCallback(ndef_file + 2, ndef_length);
                  }
                }
              }
              break;
            default:
              DMSG("Command not supported!");
              DMSG_HEX(rwbuf[C_APDU_INS]);
              DMSG("\n");
              setResponse(FUNCTION_NOT_SUPPORTED, rwbuf, &sendlen);
          }
      
          status = pn532.tgSetData(rwbuf, sendlen);
          if(status < 0){
            DMSG("tgSetData failed\n!");
            pn532.inRelease();
            return true;
          }
        }
        pn532.inRelease();
        return true;
      }
      

      【讨论】:

      • 当我以这种方式更改命令数组时,根本无法检测到卡,正如我上面已经提到的,即使是您推荐的 NXP 的应用程序也是如此。我也尝试将此处的 PN532.cpp 中的命令数组复制到 emulate 函数中,然后我什至在 Github 上得到问题中提到的“87 29”答案,但我无法成功以这种方式修改代码,该命令被解析,而是我总是得到“状态不正常 tgGetData 失败!”消息。
      • 抱歉,我不明白您下载了 NXP 应用程序。示例代码并不完美,也拒绝了好的答案。如果您收到消息“87 29”,这是一条好消息,还不错!如果电话的回答是87 29,则表示连接已关闭,因为电话打开了一个新连接(请记住,您正在模拟一张卡片!)。这时,模拟器会断电,手机会为你的模块供电(我认为)。只需重新开始一切,您就可以与手机进行通信了。
      • 我用 emulate.cpp 代码更新了帖子。请记住,这是一张个性化的卡片。该库应该提供更多选项,但似乎没有人更新此存储库...
      • 使用您的 emulate.cpp 代码,它的行为对我来说是一样的。如果我尝试按原样使用您的代码,我的手机根本检测不到该卡。如果将命令数组中的模式号更改为 0x00 或 0x01,我会收到消息“87 29”然后“状态不正常 tgGetData 失败!”错误。
      • 好的,如果它与 0x01 一起使用,则保留 0x01。但是 tgGetData 失败是可以的。在 NXP 手册第 68 页 (nxp.com/docs/en/user-guide/141520.pdf) 上,您可以看到以下消息:The PN532 configured as target has been released by its initiator 。此时,您可以在模拟卡上设置 NFC 标签,手机应该能够读取它。消息:“tgGetData 失败!”必须出现在此仿真中。
      猜你喜欢
      • 2015-07-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-11
      • 2013-06-21
      • 2020-08-05
      • 1970-01-01
      相关资源
      最近更新 更多