Эмулированная карта PN532 не читается телефоном Android - PullRequest
0 голосов
/ 12 января 2020

Я использую Arduino UNO с библиотекой elechouse и модулем PN532, подключенным через SPI.

Я пытаюсь эмулировать карту, используя пример emulate_tag_ndef в этой библиотеке, но когда я пытаюсь прочитать эмулированную карту с помощью приложения NFC Tools на моем Samsung Galaxy S7, я получаю пустой серийный номер номер, и я не получаю сообщение Ndef аналогично this .

Когда я пытаюсь изменить массив команд в библиотеке в соответствии с постом ниже по связанной проблеме на Github, мой телефон вообще не может обнаружить эмулированную карту.

PN532 работает во всех других режимах NFC (чтение / запись, peer-to-peer), все в порядке для меня.

Ответы [ 2 ]

0 голосов
/ 28 января 2020

просто раскомментируйте некоторые вещи, как я, и это должно работать.

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();
}
0 голосов
/ 13 января 2020

Посмотрите эту проблему на студии Seeeds:

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

Android телефоны будут принимать только карты Felica. Если вы добавите несколько случайных идентификаторов, они не будут обнаружены в качестве карты NDEF.

Чтобы правильно имитировать, вы должны установить идентификатор карты следующим образом:

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 card
NFCID3t может быть случайным числом.

Вы увидите, что с вашим эскизом будут другие проблемы, и иногда невозможно отладить или понять, почему он не работает (например, если у вас несколько тегов NDEF). Итак, самый важный инструмент для проверки вашего PN532 - это один из NXP:
https://play.google.com/store/apps/details?id=com.nxp.taginfolite

Это даст вам много информации. Это приложение также может читать вашу карту, даже если она неправильно отформатирована.

Редактировать: так я обновил emulatetag.cpp из библиотеки PN532 :

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;
}
...