cancel
Showing results for 
Search instead for 
Did you mean: 

Sending a select apdu message to a phone using st25r3911b

rabinniroula
Associate II

I have ported rfal platform to work on my esp32 and have successfully detected all kinds of cards. Now I want to send select APDU message to the phone but I am unsucessful at doing so.

The code I have used:

 

err = rfal_nfc.rfalIsoDepPollAHandleActivation(RFAL_ISODEP_FSXI_512, RFAL_ISODEP_NO_DID, RFAL_BR_424, &isoDepDev);
Serial.print("handle activation: ");
Serial.println(err);

err = rfal_nfc.rfalIsoDepStartApduTransceive(param);
Serial.print("APDU txrx error: ");
Serial.println(err);

if (err == ERR_NONE)
{
  do
  {
    rfal_nfc.rfalNfcWorker();
    err = rfal_nfc.rfalIsoDepGetApduTransceiveStatus();
    Serial.print(".");
    // delay(200);
    Serial.print(err);
  } while (err == ERR_BUSY);
  Serial.println("Received!");
  Serial.println(rxLen);
}

 

The param I've used is:

 

rfalIsoDepApduTxRxParam param;
rfalIsoDepApduBufFormat txapdu;
rfalIsoDepApduBufFormat rxapdu;
rfalIsoDepApduBufFormat temp;
uint16_t rxLen;
txapdu.apdu[0] = SELECT_APDU[0];
txapdu.apdu[1] = SELECT_APDU[1];
txapdu.apdu[2] = SELECT_APDU[2];
txapdu.apdu[3] = SELECT_APDU[3];
txapdu.apdu[4] = SELECT_APDU[4];
txapdu.apdu[5] = SELECT_APDU[5];
txapdu.apdu[6] = SELECT_APDU[6];
txapdu.apdu[7] = SELECT_APDU[7];
txapdu.apdu[8] = SELECT_APDU[8];
txapdu.apdu[9] = SELECT_APDU[9];
txapdu.apdu[10] = SELECT_APDU[10];
txapdu.apdu[11] = SELECT_APDU[11];
txapdu.apdu[12] = SELECT_APDU[12];
param.txBuf = &txapdu;
param.txBufLen = sizeof(txapdu);
param.rxBuf = &rxapdu;
param.rxLen = &rxLen;
param.FWT = isoDepDev.info.FWT;

param.dFWT = isoDepDev.info.dFWT;

param.FSx = RFAL_ISODEP_FSX_KEEP;      

//myParam.FSx = isoDepDevice.info.FSx;

param.DID = isoDepDev.info.DID;  
param.ourFSx = isoDepDev.info.FSx; 

 

The rxLen I get is always 42405. I couldn't find any examples to transceive apdu messages. Can you please help me? Thank you
1 ACCEPTED SOLUTION

Accepted Solutions

Hi,

 

Status codes of '90 00' are typically only used from PICC side. But of course nothing preventing a protocol to use it also the other direction.

I think the problem in your code is your repeated call to rfalIsoDepInitialize(). Once initialized and activated, just stay with the transceive of APDU. I think the initialize will have reset the 1-bit ISODEP block counter - thus the APDU ignored by the phone due to not fitting counter.

BR, Ulysses

View solution in original post

17 REPLIES 17
rabinniroula
Associate II
rfalIsoDepApduTxRxParam param;
rfalIsoDepApduBufFormat txapdu;
rfalIsoDepApduBufFormat rxapdu;
rfalIsoDepApduBufFormat temp;
uint16_t rxLen;
txapdu.apdu[0] = SELECT_APDU[0];
txapdu.apdu[1] = SELECT_APDU[1];
txapdu.apdu[2] = SELECT_APDU[2];
txapdu.apdu[3] = SELECT_APDU[3];
txapdu.apdu[4] = SELECT_APDU[4];
txapdu.apdu[5] = SELECT_APDU[5];
txapdu.apdu[6] = SELECT_APDU[6];
txapdu.apdu[7] = SELECT_APDU[7];
txapdu.apdu[8] = SELECT_APDU[8];
txapdu.apdu[9] = SELECT_APDU[9];
txapdu.apdu[10] = SELECT_APDU[10];
txapdu.apdu[11] = SELECT_APDU[11];
txapdu.apdu[12] = SELECT_APDU[12];
param.txBuf = &txapdu;
param.txBufLen = sizeof(txapdu);
param.rxBuf = &rxapdu;
param.rxLen = &rxLen;
param.FWT = isoDepDev.info.FWT;

param.dFWT = isoDepDev.info.dFWT;

param.FSx = RFAL_ISODEP_FSX_KEEP;      

//myParam.FSx = isoDepDevice.info.FSx;

param.DID = isoDepDev.info.DID;  
param.ourFSx = isoDepDev.info.FSx; 

Is there anything wrong with my param?

Hi

when exiting from the do { ... } while (err == ERR_BUSY); loop, the error code is not checked. The rfalIsoDepGetApduTransceiveStatus probably returns an ERR_TIMEOUT due to no response from the tag. When the error code is different from ERR_NONE, the rxLen is not updated and contains probably garbage from the stack.

Can you log the error code when exiting from the do while loop ?

Also, can you print the content of param.txBuf?

Note that rfal_t4t.c provides an rfalT4TPollerCompose API to build the SELECT application, SELECT file, READ BINARY and UPDATE BINARY Commands APDU. 

An example of APDU transceive is available in ndefT4TTransceiveTxRx function in ndef_t4t.c file.

Rgds

BT

In order to give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

 

#include <SPI.h>
#include "rfal_rf.h"
#include "rfal_nfc.h"
#include "rfal_rfst25r3911.h"
#include "ndef_class.h"

// Initialize SPI and RFAL instances
SPIClass dev_spi;
RfalRfST25R3911BClass rfst25r3911b(&dev_spi, 5, 17);
RfalNfcClass rfal_nfc(&rfst25r3911b);
NdefClass ndef(&rfal_nfc);

// Define constants
#define APDU_CMD_LEN 13
uint8_t SELECT_APDU[] = {}; // I can't disclose this for security reasons
ReturnCode err;
void setup() {
  Serial.begin(115200);
  dev_spi.begin();

  // Initialize RFAL
  err = rfal_nfc.rfalNfcInitialize();
  if (err != ERR_NONE) {
    Serial.println("RFAL initialization failed.");
    while (true);
  }
  Serial.println("RFAL Initialized");

  err = rfst25r3911b.rfalFieldOnAndStartGT();
  if (err != ERR_NONE) {
    Serial.println("Field activation failed.");
    while (true);
  }
  Serial.println("Field On");

  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
}

void loop() {
  rfal_nfc.rfalNfcWorker(); // RFAL worker

  // Initialize as NFC-A poller
  err = rfal_nfc.rfalNfcaPollerInitialize();
  if (err != ERR_NONE) {
    Serial.println("Poller initialization failed.");
    delay(1000);
    return;
  }
  Serial.println("Poller Initialized");

  // Check for NFC-A tag presence
  rfalNfcaSensRes sensRes;
  err = rfal_nfc.rfalNfcaPollerCheckPresence(RFAL_14443A_SHORTFRAME_CMD_REQA, &sensRes);
  if (err != ERR_NONE) {
    Serial.println("No tag detected.");
    delay(1000);
    return;
  }
  Serial.println("NFC-A tag detected.");

  // Perform collision resolution
  bool collPending = false;
  rfalNfcaSelRes selRes;
  uint8_t nfcId[10];
  uint8_t nfcIdLen;
  err = rfal_nfc.rfalNfcaPollerSingleCollisionResolution(1, &collPending, &selRes, nfcId, &nfcIdLen);
  if (err != ERR_NONE) {
    Serial.println("Collision resolution failed.");
    delay(1000);
    return;
  }
  Serial.print("Tag selected. UID: ");
  for (int i = 0; i < nfcIdLen; i++) {
    Serial.print(nfcId[i], HEX);
    Serial.print(" ");
  }
  Serial.println();

  // Activate device
  rfalIsoDepDevice isoDepDev;
  err = rfal_nfc.rfalIsoDepPollAHandleActivation((rfalIsoDepFSxI)RFAL_ISODEP_FSDI_DEFAULT, RFAL_ISODEP_NO_DID, RFAL_BR_106, &isoDepDev);
  if (err != ERR_NONE) {
    Serial.print("Handle activation error: ");
    Serial.println(err);
    delay(1000);
    return;
  }
  Serial.println("Device activated");

  // Set up APDU parameters
  rfalIsoDepApduTxRxParam param;
  rfalIsoDepApduBufFormat txapdu;
  rfalIsoDepApduBufFormat rxapdu;
  uint16_t rxLen;

  memcpy(txapdu.apdu, SELECT_APDU, sizeof(SELECT_APDU));
  param.txBuf = &txapdu;
  param.txBufLen = sizeof(SELECT_APDU);
  param.rxBuf = &rxapdu;
  param.rxLen = &rxLen;
  param.FWT = isoDepDev.info.FWT;
  param.dFWT = isoDepDev.info.dFWT;
  param.FSx = RFAL_ISODEP_FSX_KEEP;
  param.DID = isoDepDev.info.DID;
  param.ourFSx = isoDepDev.info.FSx;

  Serial.println("Sending APDU...");
  err = rfal_nfc.rfalIsoDepStartApduTransceive(param);
  if (err != ERR_NONE) {
    Serial.print("Error in transceive: ");
    Serial.println(err);
    delay(1000);
    return;
  }

  // Wait for response
  do {
    rfal_nfc.rfalNfcWorker();
    err = rfal_nfc.rfalIsoDepGetApduTransceiveStatus();
  } while (err == ERR_BUSY);

  if (err != ERR_NONE)
  {
    Serial.printf("Error in response: %d \n", err);
    return;
  }

  // if (err != ERR_NONE) {
  //   Serial.print("Error receiving APDU response: ");
  //   Serial.println(err);
  // } else {
  //   Serial.println("APDU Response received:");
  //   for (uint8_t i = 0; i < rxLen; i++) {
  //     Serial.print(rxapdu.apdu[i], HEX);
  //     Serial.print(" ");
  //   }
  //   Serial.println();
  // }

  // delay(1000); // Add delay between polling attempts
}

This is the full code that I'm using, there's no transceive happening. No response the length of the response is 0. What did I do wrong here?

 

Hi,

what is the return code of rfalIsoDepStartApduTransceive? what is the return code of rfalIsoDepGetApduTransceiveStatus when exiting the do while loop?

Rgds

BT

In order to give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

It's ERR_TIMEOUT (4). When I send the same APDU message from a PN532 using the indataexchange() function, I get a response back but it isn't working with st25r3911b

Hi,

the tmpBuf is not initialized. it should point to a rfalIsoDepBufFormat buffer. Anyway, I believe this is not the cause of the no response issue. Can you connect a logic analyzer on the SPI (CLK, MOSI, MISO, SS) + IRQ_3911B and send us the log file?

Thanks

Rgds

BT

In order to give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

I did initialize tmpBuf and added it to param. But it still is not working, I don't have a logic analyzer at my disposal. Can you suggest some other method?

Hi,

I would try the following:

  1. I don't see rfalIsoDepInitialize[WithParams]() being called - please add it if not there.
  2. Put a breakpoint into st25r3911WriteFifo() and inspect the frame which is being sent to the FIFO. Typically it should be your APDU prepended with "0x02" (Protocol Control Byte of ISODEP layer).
  3. Inspect your timer abstraction - maybe MCU internal timeouts are signaled too early. You can also enable the ST25R_SELFTEST which performs some basic tests on the timers. But maybe your problem is only for longer timeouts...

BR, Ulysses

I believe I should call the rfalIsoDepInitializeWithParams() should be called only once or should it be in the loop?

 

rfal_nfc.rfalIsoDepInitializeWithParams(RFAL_COMPLIANCE_MODE_EMV, 20, 20, 20, 20);
 
I don't have the hardware with me right now to test this. Will do tomorrow. Thanks for the help btw.