Skip to main content
Visitor II
July 27, 2022
Solved

Cr95hf card emulation help me plz

  • July 27, 2022
  • 10 replies
  • 3182 views

Hello, I tried to implement the card emulator using CR95HF to refer to the datasheet, but it failed. Could you provide the sample code data for the cmd read write cmd procedure in the form of Hex value? I'd appreciate your help.

Ex)

Write cmd 0x 02 04 68 41

Read cne 0x 01. . .

Card emulator write cmd 0x02008. Etc

    This topic has been closed for replies.
    Best answer by Brian TIDAL

    Hi

    this is a great achievement:

    0x00, 0x80, 0x05, 0xe0, 0x80, 0x31, 0x73, 0x08, 0x00, 0x00 is basically the E0 80 RATS frame (Request for Answer To Select) that is sent by the Android Phone after the anti collision and the activation. This means the anti collision (AC Filter) is properly working and the Android Phone is now trying to move to ISO14443-4 layer (aka ISO-DEP) with the RATS command. I see that you send the ATS answer (05 78 80 80 00), then make sure to then send the Listen command to trigger the reception of the next command. Note that the logging of the various exchanges may have an impact on the real time: for example the phone is waiting for the ATS within a certain timeout, if the logging through UART is delaying too much the sending of the ATS, the waiting time on phone side may elapse and this can cause a communication failure.

    Rgds

    BT

    10 replies

    Technical Moderator
    July 27, 2022

    Hi,

    here is an example of Type 4 Card Emulation:

    ;protocolSelect Card emulation for ISO/IEC 14443 Type A
    >>02 02 12 0A
    << 00 00
     
    ;activate anticollition filter ATQA=02 00 SAK=20 (ISO14443-4 compliant) 4 bytes UID = 5F 53 54 4D
    >> 0D 07 02 00 20 5F 53 54 4D
    << 00 00
     
    ;listen
    >> 05 00
    << 00 00
     
    ;wait for IRQ_OUT (or use SPI poll)
    ;when data are received from the reader:
    << 80 05 E0 70 BE 84 08
     
    ;a RATS command has been received (E0 70)
    ;send ATS answer to RATS
    >> 06 06 05 78 80 80 00 28
    << 00 00
     
    ;listen
    >> 05 00
    << 00 00
     
    ;wait for IRQ_OUT (or use SPI poll)
    ;when data are received from the reader:
    << 80 11 02 00 A4 04 00 07 D2 76 00 00 85 01 01 00 35 C0 08
     
    ;an ISO 7816 Select application has been received (00 A4 04 00 07 D2 76 00 00 85 01 01 00)
    ;send the 90 00 status word response 
    >> 06 04 02 90 00 28
    << 00 00
     
    ;listen
    >> 05 00
    << 00 00
    ;wait for IRQ_OUT (or use SPI poll)

    Rgds

    BT

    DJeon.4Author
    Visitor II
    August 16, 2022

    Thank you for your answer. I wrote it through a translator because I am not good at English.

    My reply was late because of personal matters.

    We tried to implement the card emulator mode by referring to the data you provided, but failed.

    I will provide you with the Arduino code I wrote, so I would appreciate it if you could help me implement the CR95HF card emulator mode.

    1. first GPIO Pin and SPI mode Setting

    <Set code >

    // Configure GPIO pins : CS = 15, SCK=5, MOSI=6, MISO=7, IRQ IN= 2, IRQ OUT = 1

      if (CS != -1)     { pinMode(CS, OUTPUT);          Serial.println("(1). SPI Chip Select OK");  }  

      if (IRQ_IN != -1) { pinMode(IRQ_IN, OUTPUT);      Serial.println("(2). SPI Interrupt Request Ok");}    

      if (SSI_0 != -1)  { pinMode(SSI_0, OUTPUT);       Serial.println("(3). SPI SSI_0 Pin Ok");}

      if (SSI_0 == -1)  {                               Serial.println("(3). SPI SSI_0 Pin No use");}  

      if (SSI_1 != -1)  { pinMode(SSI_1, OUTPUT);       Serial.println("(4). SPI SSI_1 pin Ok");}    

      if (SSI_1 == -1)  {                               Serial.println("(4). SPI SSI_1 pin No use");}    

      // Set in SPI mode

      if (SSI_0 != -1)  { digitalWrite(SSI_0, HIGH); }    

      if (SSI_1 != -1)  { digitalWrite(SSI_1, LOW); }  

      if (IRQ_IN != -1) { digitalWrite(IRQ_IN, HIGH);}

       

      delay(1);

      if (IRQ_IN != -1) { digitalWrite(IRQ_IN, LOW); delay(1);  }

      if (IRQ_IN != -1) { digitalWrite(IRQ_IN, HIGH); delay(1); }

      SPI.begin(); // arduino sketch SPI begin

     

      while (!EchoResponse()) {  

        if (IRQ_IN != -1) { digitalWrite(IRQ_IN, HIGH); delay(1);}

        if (IRQ_IN != -1) { digitalWrite(IRQ_IN, LOW); delay(1);}

      }​

    2. function code

    char RFID_CR95HF::EchoResponse() {

      digitalWrite(CS, LOW);

      SPI.beginTransaction(SETTINGS);

      SPI.transfer(0x00);  // Send cmd to CR95HF

      SPI.transfer(ECHO);

      SPI.endTransaction();

      digitalWrite(CS, HIGH);

     

      while (1) {

        digitalWrite(CS, LOW);

        SPI.beginTransaction(SETTINGS);

        SPI.transfer(0x03);

        tmp = SPI.transfer(0);

        SPI.endTransaction();

        digitalWrite(CS, HIGH);

        if ((tmp & 0x08) >> 3) {

          digitalWrite(CS, LOW);

          SPI.beginTransaction(SETTINGS);

          SPI.transfer(0x02);

          tmp = SPI.transfer(0);

          SPI.endTransaction();

          digitalWrite(CS, HIGH);

          if (tmp == ECHO) {

            return 1;

          }

          return 0;

        }

      }

    }

    void RFID_CR95HF::writeCmd(unsigned short cmd, unsigned short dataLen) {

      unsigned short i = 0;

      digitalWrite(CS, LOW);

      SPI.beginTransaction(SETTINGS);

      SPI.transfer(0x00);  // contol byte  :

      SPI.transfer(cmd);   // command

      SPI.transfer(dataLen); // length

      while (dataLen == 0) {

        digitalWrite(CS, HIGH);

        break;

      }

      Serial.printf("write cmd :");

      for (i = 0; i < dataLen; i++) {

        SPI.transfer(sdata[i]);

       Serial.printf("0x%x ", sdata[i]);

      }

      Serial.println("");

      SPI.endTransaction();

      digitalWrite(CS, HIGH);

      delay(10);

    }

    // Poll the CR95HF

    void RFID_CR95HF::readCmd() {

      unsigned short i = 0;

      while (1)

      {

        digitalWrite(CS, LOW);

        SPI.beginTransaction(SETTINGS);

        SPI.transfer(0x03);

        res = SPI.transfer(0);

        SPI.endTransaction();

        digitalWrite(CS, HIGH);

          //Serial.printf("==== Read cmd :");

          for (i = 0; i < dataNum; i++) {

            rdata[i] = SPI.transfer(0);

          //  Serial.printf("0x%x ", rdata[i]);

          }    

        if ((res & 0x08) >> 3) {

          digitalWrite(CS, LOW);

          SPI.beginTransaction(SETTINGS);

          SPI.transfer(0x02);

          res = SPI.transfer(0);

          dataNum = SPI.transfer(0);

          Serial.printf("Read cmd :");

          for (i = 0; i < dataNum; i++) {

            rdata[i] = SPI.transfer(0);

            Serial.printf("0x%x ", rdata[i]);

          }

          Serial.println("");

          SPI.endTransaction();

          digitalWrite(CS, HIGH);

          break;

        }

        SPI.endTransaction();

        digitalWrite(CS, HIGH);

        delay(10);

      }

    }

    ​=============================================

    Function usage is as follows

    <example card emulation mode packet >

    1. ;protocolSelect Card emulation for ISO/IEC 14443 Type A
    2. >>02 02 12 0A
    3. << 00 00

    sdata[0] = 0x12; // data 0

    sdata[1] = 0x0A; // data 1

    writeCmd(0x02, 2); // protocol select cmd, length SPI write

    readCmd() // SPI Read

    0693W00000QNlkzQAD.png 

    <debug result>

    0693W00000QNllOQAT.png 

    Technical Moderator
    August 17, 2022

    Hi,

    please use a logic analyzer and verify the sequence against the one provided by Brian. If it still doesn't work please provide the logic analyzer traces.

    Best Regards, Ulysses

    DJeon.4Author
    Visitor II
    August 17, 2022

    Please refer to the article below

    Technical Moderator
    August 17, 2022

    Hi,

    hard to follow your sequences. Please write full sequences as described by Brian and please use a Logic Analyzer to follow the timing of your commands as well.

    Best Regards, Ulysses

    DJeon.4Author
    Visitor II
    August 18, 2022

    This is the order in which my code works

    1. CS, IRQ_IN, pin enable
    2. SPI begin setup -> function "SPI.begin();"
    3. CR95HF wake up setup

    1) slave(CS) pin Low -> SPI Setting() -> packet Tx : 0x00 (control byte), 0x55(ECHO cmd), 0x00 (len)

    -> slave Pin High -> SPI.endTransaction

    ※Tx packet : 0x00, 0x55, 0x00

    2) slave(CS) pin Low -> SPI Setting() -> SPI transfer() packet Tx : 0x02(control byte), 0x00 -> slave Pin High

    ->SPI.endTransaction

    ※ Tx packet : 0x02, 0x00

    ​ 4. Read Chip ID (CR95HF)

    1) slave(CS) pin Low -> SPI Setting(); ->SPI transfer() packet Tx : 0x00(control byte), 0x01 (IDN cmd), 0x00(len)

    -> slave Pin High -> SPI.endTransaction

    ※Tx packet : 0x00, 0x51, 0x00

    2) slave(CS) pin Low -> SPI Setting(); -> packet Tx : 0x03(control byte), 0x00

    -> slave Pin High -> SPI.endTransaction -> for( i=0; i<data; i++) save[i] = SPI transfer(); packet Tx : 0x03,

    0x00

    ※Tx packet : 0x03, 0x00 after -> if (SPI transfer & 0x08) -> slave(CS) pin Low -> SPI Setting(); -> packet

    Tx : 0x02(control byte), 0x00 -> for(i=0; i<data; i++) rdata[i]=SPI transfer(); -> slave Pin High

    -> SPI.endTransaction

    ※Tx packet : 0x02, 0x00

    ※Rx packet(rdata[]) : 0x4e 0x46 0x43 0x20 0x46 0x53 0x32 0x4a 0x41 0x53 0x54 0x34 0x0 0x2a 0xce 

    result : RFID Reader CR95HF IDN : NFC FS2JAST4

    ​ 5. Card Emulation Mode Set up

    1) select protocol

    Tx packet : 0x00 (controlbyte), 0x02(protocol select cmd), 0x02(len), 0x12, 0x08

    rx packet : 0x00 should be returned if read normally, but no actual value is returned

    2) AC filter

    Tx packet : 0x00( controlbyte), 0x0D(AC_filter cmd), 0x07(len), 0x02, 0x00, 0x20, 0x5F(UID), .0x53(UID),

    0x54(UID), 0x4D(UID)

    Rx packet : 0x00 should be returned if read normally, but no actual value is returned

    3) Listen : I wrote a write code, but this part is not written :sad_but_relieved_face:

    Tx packet : 0x00(controlbyte), 0x05 (listen cmd), 0x00 (len)

    Rx :0x00 should be returned if read normally, but no actual value is returned

    4) Tag Send

    Tx packet :0x00(control byte), 0x06, 0x06, 0x05, 0x78, 0x80, 0x80, 0x00, 0x28

    Rx : 0x00 should be returned if read normally, but no actual value is returned

    I've written it up to here

    0

    0693W00000QNwC0QAL.png

    ​Since there is no logic analyzer, we replaced it with a terminal window provided by Arduino Integrated Development Environment

    card emulation code : help me plz :sad_but_relieved_face:

    void card_Emulation_14443A(void)

    {

     Serial.println("Caed Emulation Mode Set up");

     sdata[0] = 0x12;

     sdata[1] = 0x08;

     writeCmd(ProtocolSelect, 2); // Card Emulation Mode Tag type select 14443A

     readCmd();

     sdata[0] = 0x02; //ATQA

     sdata[1] = 0x00;

     sdata[2] = 0x20; // sak

     sdata[3] = 0x5F; // User Tag UID

     sdata[4] = 0x53; // User Tag UID

     sdata[5] = 0x54; // User Tag UID

     sdata[6] = 0x4D; // User Tag UID

     writeCmd(AC_filter ,7);

     readCmd();

     writeCmd(Listen ,0);

     readCmd();

      

     while(!EchoResponse()) {

      if (IRQ_IN != -1) { digitalWrite(IRQ_IN, HIGH); delay(10);}

      if (IRQ_IN != -1) { digitalWrite(IRQ_IN, LOW); delay(10);}

      readCmd();

     }

     sdata[0] = 0x05;

     sdata[1] = 0x78;

     sdata[2] = 0x80;

     sdata[3] = 0x80;

     sdata[4] = 0x00;

     sdata[5] = 0x28;

     writeCmd(Tag_Send ,6);

     readCmd();

     writeCmd(Listen ,0);

     readCmd();

      

    }

    DJeon.4Author
    Visitor II
    August 18, 2022

    -

    Technical Moderator
    August 18, 2022

    Hi,

    I think you will need a logic analyzer in the end. Changing the low level/porting will require looking at signals (SPI+IRQ_IN+IRQ_OUT, etc.) and timing. If you are lucky then you can get it working without but experience is that often there are subtle problems in SPI, IRQ handing, timing which cause it not working.

    Of course you can go, create hypothesis of what could be the reason or what is happening, change something here and there and you may get it working. But a logic analyzer will tell you straight away what is happening and removes a lot of guessing.

    There are inexpensive USB based logic analyzers, please get one!

    Best Regards, Ulysses

    DJeon.4Author
    Visitor II
    August 18, 2022

    Thank you for your late reply.

    Can you provide sample code data for the cmd read write cmd procedure in Hex value format? I'd appreciate your help. The entire process from CR95HF start to card emulator activation is required.

    I may have entered the wrong cmd due to lack of code data while implementing it. What I can do at this point is look at the data in the form of cmd read write hex values and implement it throughout the process. I'd appreciate your help.

    ex)

    CR95HF set ~ Full process Hex cmd data from card emulator activation

    cmd 0x02046841 write

    Read cmd 0x01, 0x04, 0x80.

    Card emulator write cmd 0x02008. etc

    Graduate II
    August 18, 2022

    Perhaps don't print data as you're sending and receiving it. ie​ do the CS, SPI interaction quickly and quietly, printing the whole transaction subsequently

    DJeon.4Author
    Visitor II
    September 1, 2022

    There is no logic analyzer. So I can't provide itten thousand

    I will check the AC filter

    ​I will modify the code and get packet data including control byte, command, and length. Please wait a moment

    Technical Moderator
    August 22, 2022

    Hi,

    make sure to follow the SPI communication protocol as described in §4.1 of the ST25R95 datasheet. In particular, before reading the response to a command, "the application software needs to wait for the ST25R95 to be ready to send the response. Not following this procedure may cause unpredictable behavior". This can be done by:

    • Polling the ST25R95 until it is ready (0x03 poll control byte)
    • waiting for IRQ_OUT: this is what I usually recommend (very simple to use)

    So, after sending "0x00 0x02 0x02 0x12 0x0A" (CE mode ProtocolSelect), make sure to poll the ST25R95 or to wait for the IRQ_OUT before reading the response to the command.

    The  RFID_CR95HF::readCmd() looks erroneous:

    // Poll the CR95HF
    void RFID_CR95HF::readCmd() {
     unsigned short i = 0;
     
     while (1)
     {
     digitalWrite(CS, LOW);
     SPI.beginTransaction(SETTINGS);
     SPI.transfer(0x03);
     res = SPI.transfer(0);
     SPI.endTransaction();
     digitalWrite(CS, HIGH);
     
     //Serial.printf("==== Read cmd :");
     for (i = 0; i < dataNum; i++) { <----!! Use of unitialized value !!
     rdata[i] = SPI.transfer(0); <---- !! Icorrect SPI read while CS is high !!
     // Serial.printf("0x%x ", rdata[i]);
     } 
     
     if ((res & 0x08) >> 3) {
     digitalWrite(CS, LOW);
     SPI.beginTransaction(SETTINGS);
     SPI.transfer(0x02);
     res = SPI.transfer(0); <---- !! please print the res value !!
     dataNum = SPI.transfer(0); <---- !! please print the dataNum value !!
     Serial.printf("Read cmd :");
     for (i = 0; i < dataNum; i++) {
     rdata[i] = SPI.transfer(0);
     Serial.printf("0x%x ", rdata[i]);
     }
     Serial.println("");
     SPI.endTransaction();
     digitalWrite(CS, HIGH);
     break;
     }
     
     SPI.endTransaction();
     digitalWrite(CS, HIGH);
     delay(10);
     }
    }

    After sending the protocolSelect, the res value should be 0x00 (meaning response OK) and the dataNum should be 0 (no payload). Same for the AC filter command and for the listen command.

    See examples from logic analyzer:

    0693W00000QOEOsQAP.jpg 

    0693W00000QOEOYQA5.jpg 

    Rgds

    BT

    DJeon.4Author
    Visitor II
    September 1, 2022

    Hello.

    Brian TIDAL

    We proceeded according to the sequence you told us but failed to implement the card emulation mode.

    Please check if the cmd I entered is correct.

    1. spi setting
    2. cr95hf wake up ( echo Response)
    • irq_in : high , irq_out : high , ss:low set
    • packet tx : 0x00, 0x55,0x00 -> ss:high , spi endTransaction
    • packet rx : 0x02, 0x00 -> ss:high, spi endTransaction

    1. card emulation mode setup protocol select (14443a)
    • irq_in : high , irq_out : high , ss:low set
    • packet tx : 0x00, 0x02, 0x02, 0.x12, 0x0A -> ss:high, spi endTransaction
    • irq_in : high , irq_out : low , ss:low set
    • packet rx start cmd packet tx : 0x02 ,0x00
    • packet rx : 0x00, 0x00 -> ss:high, irq_OUT: high, spi endTransaction

    2.card emulation mode ATQA and User Tag UID defind

    • irq_in : high , irq_out : high , ss:low set
    • packet tx : 0x00, 0x0D, 0x07, 0x02, 0x00, 0x20, 0x5f, 0x53, 0x54, 0x4D(user Tag UID) -> ss:high, spi endTransaction
    • irq_in : high , irq_out : low , ss:low set
    • packet rx start cmd packet tx : 0x02 ,0x00
    • packet rx : 0x00, 0x00 -> ss:high, irq_OUT: high, spi endTransaction

    3.Listen

    • irq_in : high , irq_out : high , ss:low set
    • packet tx :0x05, 0x00 -> ss:high, spi endTransaction
    • irq_in : high , irq_out : low , ss:low set
    • packet rx start cmd packet tx : 0x02 ,0x00
    • packet rx : 0x00, 0x00 -> ss:high, irq_OUT: high, spi endTransaction

    That's all I've done

    ​Brian TIDAL's sequence changes from 4. to the mobile phone Tag reader mode and recognizes CR95HF, but it doesn't work. I'm not sure where it went wrong.

    ​Help me

    ====================================

    <​Before contacting the CR95HF in Samsung mobile NFC reader mode...>

    0693W00000SuHZWQA3.png<after...>

    0693W00000SuHaUQAV.png 

    ==================================

    <card emulation mode code>

    void card_Emulation_14443A(void)
    {
     Serial.println("Caed Emulation Mode Set up");
     sdata[0] = 0x12;
     sdata[1] = 0x0A;
     writeCmd(ProtocolSelect, 2); // Card Emulation Mode Tag type select 14443A
     delay(10);
     CardEmulate_readCmd();
     
     
     
     sdata[0] = 0x02; //ATQA
     sdata[1] = 0x00;
     sdata[2] = 0x20; // sak
     sdata[3] = 0x5F; // User Tag UID
     sdata[4] = 0x53; // User Tag UID
     sdata[5] = 0x54; // User Tag UID
     sdata[6] = 0x4D; // User Tag UID
     writeCmd(AC_filter ,7); 
     delay(10);
     CardEmulate_readCmd(); 
     
     writeCmd(Listen ,0);
     delay(100);
     CardEmulate_readCmd();
     delay(100);
     
     sdata[0] = 0x05;
     sdata[1] = 0x78;
     sdata[2] = 0x80;
     sdata[3] = 0x80;
     sdata[4] = 0x00;
     sdata[5] = 0x28;
     writeCmd(Tag_Send ,6); 
     delay(10); 
     CardEmulate_readCmd();
     
     writeCmd(Listen ,0);
     delay(10);
     CardEmulate_readCmd();
     
    }
     
    void CardEmulate_readCmd(void) 
    {
     unsigned short i = 0;
     while (1) 
     {
     digitalWrite(IRQ_IN, HIGH);
     digitalWrite(IRQ_OUT, LOW);
     digitalWrite(CS, LOW);
     SPI.beginTransaction(SETTINGS);
     SPI.transfer(0x02); // read data
     res = SPI.transfer(0);
     dataNum = 10;
     Serial.printf("Read cmd :");
     for (i = 0; i < dataNum; i++) {
     rdata[i] = SPI.transfer(0);
     Serial.printf("0x%x ", rdata[i]);
     }
     Serial.println("");
     SPI.endTransaction();
     digitalWrite(CS, HIGH);
     digitalWrite(IRQ_OUT, HIGH);
     break;
     delay(10);
     }
    }
     
    void writeCmd(unsigned short cmd, unsigned short dataLen) 
    {
     unsigned short i = 0;
     digitalWrite(IRQ_IN, HIGH);
     digitalWrite(IRQ_OUT, HIGH);
     digitalWrite(CS, LOW);
     SPI.beginTransaction(SETTINGS);
     SPI.transfer(0x00); // contol byte :
     SPI.transfer(cmd); // command
     SPI.transfer(dataLen); // length
     while (dataLen == 0) {
     digitalWrite(CS, HIGH); 
     Serial.println(" data len 0 ");
     break;
     }
     Serial.printf("write cmd :");
     for (i = 0; i < dataLen; i++) {
     SPI.transfer(sdata[i]);
     Serial.printf("0x%x ", sdata[i]);
     }
     Serial.println("");
     SPI.endTransaction();
     digitalWrite(CS, HIGH);
     delay(10);
    }

    Technical Moderator
    September 1, 2022

    Hi,

    can you confirm that the protocolSelect (CE mode) command properly returns 0x00 0x00 and that AC Filter command also returns 0x00 0x00?

    Can you provide a LogicAnalyzer trace of SPI CLK/MOSI/MISO/SS + IRQ_OUT?

    Thanks

    Rgds

    BT

    DJeon.4Author
    Visitor II
    September 2, 2022

    4. When the wait for IRQ recognizes the mobile phone, it returns the value as follows.

    0x00, 0x80, 0x05, 0xe0, 0x80, 0x31, 0x73, 0x08, 0x00, 0x00

    0693W00000SuMLPQA3.png

    Technical Moderator
    September 2, 2022

    Hi

    this is a great achievement:

    0x00, 0x80, 0x05, 0xe0, 0x80, 0x31, 0x73, 0x08, 0x00, 0x00 is basically the E0 80 RATS frame (Request for Answer To Select) that is sent by the Android Phone after the anti collision and the activation. This means the anti collision (AC Filter) is properly working and the Android Phone is now trying to move to ISO14443-4 layer (aka ISO-DEP) with the RATS command. I see that you send the ATS answer (05 78 80 80 00), then make sure to then send the Listen command to trigger the reception of the next command. Note that the logging of the various exchanges may have an impact on the real time: for example the phone is waiting for the ATS within a certain timeout, if the logging through UART is delaying too much the sending of the ATS, the waiting time on phone side may elapse and this can cause a communication failure.

    Rgds

    BT