0

I am using Arduino UNO with elechouse's library and a PN532 module connected through SPI.

I am trying to emulate a card using the emulate_tag_ndef example in that library, but when I try to read the emulated card with the NFC Tools app on my Samsung Galaxy S7, I get an empty serial number and I don't get the Ndef message similarly to this.

When I try to change the command array in the library according to the post lower on the linked issue on GitHub, then the emulated card cannot be detected by my phone at all.

The PN532 works alright in all other NFC modes (read/write, peer-to-peer) for me.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Martin Schmied
  • 84
  • 1
  • 11

3 Answers3

1

After trying a lot of possible workarounds, I can bravely claim that Android only works in HCE mode with PN532 and iOS only works with NDEF.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Daniel Pal
  • 21
  • 5
0

Give a look to this issue on Seeeds studio:

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

Android phones will only accept Felica Cards. If you put some random IDs, it will not be detected as NDEF card.

To emulate it right, you have to set the card ID in this way:

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 is the card UID, you can set something on your sketch.
NFCID2t has to be exactly how I wrote in the code above => Felica card
NFCID3t can be some random numbers.

You will see that there will be other issues with your sketch and sometimes it is impossible to debug or see why it does not work (for example if you have multiple NDEF Tags). So, the most important tool to test your PN532 is this one from NXP:
https://play.google.com/store/apps/details?id=com.nxp.taginfolite

This will give you a lot of info. This app can also read your card, even if it is not formatted in the right way.

Edit: This is how I updated the emulatetag.cpp from PN532 library:

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;
}
Adriano
  • 1,743
  • 15
  • 28
  • When I change the command array in this way, the card cannot be detected at all, as I have already mentioned above, even by the app from NXP, which you have recommended. I have also tried to copy the command array from the PN532.cpp here into the emulate function, then I even get the "87 29" answer mentioned in the issue on Github, but I could not succeed in modifying the code in such a way, that this command gets parsed and instead I always get "status is not ok tgGetData failed!" message. – Martin Schmied Jan 14 '20 at 21:33
  • Sorry, I didn't understood that you downloaded the NXP app. The example code is not perfect and rejects also good answers. If you get the message "87 29" it is a good message, not bad! If the answer from phone is `87 29` it means that the connection was closed, as the phone opened a new one (remember that you are emulating a card!). At this time, the emulator will remove the power and the phone will power your module (I think). Just start everything again and you will be able to communicate with your phone. – Adriano Jan 15 '20 at 10:49
  • I updated the post with the emulate.cpp code. Just remember that this is a personalized card. The library should give more options but it seems that nobody is updating this repository... – Adriano Jan 15 '20 at 11:06
  • With your emulate.cpp code it behaves the same for me. If I try to use your code as is, my phone does not detect the card at all. If change the mode number in the command array to 0x00 or 0x01, I get the message "87 29" and then the "status is not ok tgGetData failed!" error. – Martin Schmied Jan 15 '20 at 15:36
  • Ok, if it works with 0x01, leave 0x01. But the tgGetData failed is ok. On the NXP manual, page 68 (https://www.nxp.com/docs/en/user-guide/141520.pdf) you can see this message: `The PN532 configured as target has been released by its initiator `. At this point, you can set your NFC Tag on your emulated card and the phone should be able to read it. The message: "tgGetData failed!" must appear on this emulation. – Adriano Jan 15 '20 at 16:27
  • If suppress the "status is not ok tgGetData failed!" error, I get a "Command not supported!" message instead, as the library does not know the 29 command. But I also cannot just configure the 29 to send the NDEF data using the existing "memcpy(rwbuf, ndef_file + p1p2_length, lc); setResponse(COMMAND_COMPLETE, rwbuf + lc, &sendlen, lc);" code, since the PN532 has not received any P1, P2 and LC bytes. – Martin Schmied Jan 19 '20 at 16:50
0

just uncomment some stuff like I did and it should work.

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();
}