Near Field Communication (NFC) with the Arduino

nfc_arduino

Near Field Communication, or NFC, with the Arduino can be accomplished using our BM019 serial to NFC converter module.

NFC is used for short range exchanges of data.  It can be used to read and write to smart cards and to interface with parasitically powered EEPROM.  One interesting application of NFC is the post production programming of variables into  electronic assemblies.  You could, for example, program a set of variables into a motion control module by waving a NFC master unit over an EEPROM located on an unpowered board, and adjust the EEPROM contents.

The BM019 module carries ST Micro’s CR95HF serial to NFC converter as well as other circuitry including a tuned antenna.  The CR95HF can operate with a serial UART (8N1 57.6KBPS) or with SPI (serial peripheral interface).

When I use the Arduino for serial data applications I like to use the software serial functions.  This leaves the hardware serial port open for both programming and diagnostics.  Unfortunately, the software serial functions don’t fare too well when pushed beyond 9600BPS (baud rate).  The BM019 works at too high a speed, so I opted to use interface the Arduino using the BM019 SPI port.

One issue I had to address was the voltage difference between the Arduino and the BM019.  The Arduino operates with 5V logic and the BM019 with 3.3V logic.  Therefore all of the inputs to the BM019 need to be reduced from 5V to 3.3V.  This is easily done with resistor voltage dividers.  I also slowed the SPI clock since I had added resistors to the interface.  If you wanted to run the communication faster you could use a voltage level translator.

Here’s the schematic for the connections.  It is pretty simple (click image for clearer view).

NFC_ARDUINO_UNO_SCHEMATIC

The CR95HF has a quirky power-up process.  After power-up you need to send it a wake-up pulse.  This pulse tells it to which serial interface to use (SPI or UART).  The pulse must be sent on the DIN pin of the UART interface.  If you’re using the UART you just send a 0x00 after power up.  That’s easy.  But if you are using SPI you have to allocate another connection to the BM019 to accomplish the wake-up pulse.  To operate in SPI mode you tie SS_0 to 3.3V and provides a short low pulse on the DIN pin (note: the image below shows powering up in UART mode, but provides useful timing requirements).

clip_image002

Operating the Arduino in SPI mode requires that we setup the i/o’s correctly, enable the SPI functions, and then fire off the wake-up pulse.

/*
NFC Communication with the Solutions Cubed, LLC BM019 
and an Arduino Uno.  The BM019 is a module that
carries ST Micro's CR95HF, a serial to NFC converter.

Wiring:
 Arduino          BM019
 IRQ: Pin 9       DIN: pin 2
 SS: pin 10       SS: pin 3
 MOSI: pin 11     MOSI: pin 5 
 MISO: pin 12     MISO: pin4
 SCK: pin 13      SCK: pin 6

 */

// the sensor communicates using SPI, so include the library:
#include <SPI.h>

const int SSPin = 10;  // Slave Select pin
const int IRQPin = 9;  // Sends wake-up pulse
byte TXBuffer[40];    // transmit buffer
byte RXBuffer[40];    // receive buffer
byte NFCReady = 0;  // used to track NFC state

void setup() {
    pinMode(IRQPin, OUTPUT);
    digitalWrite(IRQPin, HIGH); // Wake up pulse
    pinMode(SSPin, OUTPUT);
    digitalWrite(SSPin, HIGH);

    Serial.begin(9600);
    SPI.begin();
    SPI.setBitOrder(MSBFIRST);
    SPI.setClockDivider(SPI_CLOCK_DIV32);

 // The CR95HF requires a wakeup pulse on its IRQ_IN pin
 // before it will select UART or SPI mode.  The IRQ_IN pin
 // is also the UART RX pin for DIN on the BM019 board.

    delay(10);                      // send a wake up
    digitalWrite(IRQPin, LOW);      // pulse to put the 
    delayMicroseconds(100);         // BM019 into SPI
    digitalWrite(IRQPin, HIGH);     // mode 
    delay(10);
}

Once that’s done we need to find out if the CR95HF is correctly connected to the Arduino.  A good way to do that is to implement the IDN Command in the CR95HF.  This command returns the ID and ROM CRC for the CR95HF.  The SPI Interface is a bit tricky for this part.  You end up embedding three protocols in order to get communication.  Much of those details are in the datasheet for the BM019, in the CR95HF datasheet, or located in documents specific to the NFC protocol you are using.

In short, using SPI with the CR95HF is a three step process.  First, you send your command.  Second, you poll the CR95HF to see if a response is ready for you.  Thirdly, you read the command.  In the code below we send the IDN command to the CR95HF and display the results using the serial monitor program.  Obviously a lot of code could be removed if we didn’t use the serial monitor.

/* IDN_Command identifies the CR95HF connected to the Arduino.
This requires three steps.
1. send command
2. poll to see if CR95HF has data
3. read the response

If the correct response is received the serial monitor is used
to display the CR95HF ID number and CRC code.  This rountine is 
not that useful in using the NFC functions, but is a good way to 
verify connections to the CR95HF. 
*/
void IDN_Command()
 {
 byte i = 0;

// step 1 send the command
  digitalWrite(SSPin, LOW);
  SPI.transfer(0);  // SPI control byte to send command to CR95HF
  SPI.transfer(1);  // IDN command
  SPI.transfer(0);  // length of data that follows is 0
  digitalWrite(SSPin, HIGH);
  delay(1);

// step 2, poll for data ready
// data is ready when a read byte
// has bit 3 set (ex:  B'0000 1000')

  digitalWrite(SSPin, LOW);
  while(RXBuffer[0] != 8)
    {
    RXBuffer[0] = SPI.transfer(0x03);  // Write 3 until
    RXBuffer[0] = RXBuffer[0] & 0x08;  // bit 3 is set
    }
  digitalWrite(SSPin, HIGH);
  delay(1);

// step 3, read the data
  digitalWrite(SSPin, LOW);
  SPI.transfer(0x02);   // SPI control byte for read         
  RXBuffer[0] = SPI.transfer(0);  // response code
  RXBuffer[1] = SPI.transfer(0);  // length of data
  for (i=0;i<RXBuffer[1];i++)      
      RXBuffer[i+2]=SPI.transfer(0);  // data
  digitalWrite(SSPin, HIGH);
  delay(1);

  if ((RXBuffer[0] == 0) & (RXBuffer[1] == 15))
  {  
    Serial.println("IDN COMMAND-");  //
    Serial.print("RESPONSE CODE: ");
    Serial.print(RXBuffer[0]);
    Serial.print(" LENGTH: ");
    Serial.println(RXBuffer[1]);
    Serial.print("DEVICE ID: ");
    for(i=2;i<(RXBuffer[1]);i++)
    {
      Serial.print(RXBuffer[i],HEX);
      Serial.print(" ");
    }
    Serial.println(" ");
    Serial.print("ROM CRC: ");
    Serial.print(RXBuffer[RXBuffer[1]],HEX);
    Serial.print(RXBuffer[RXBuffer[1]+1],HEX);
    Serial.println(" ");
  }
  else
    Serial.println("BAD RESPONSE TO IDN COMMAND!");

  Serial.println(" ");
}

 

Once you know the CR95HF is connected (and your wiring matches the schematic) you can program the CR95HF for a specific NFC protocol.  This requires you to send the Set Protocol command.  In the example here I used the ISO/IEC 15693 protocol.  A variety of settings are associated with the various NFC protocols you can use.  The settings programmed here work with ST Micro’s LRi2K smart cards and their M24L series of dual interface EEPROM.

/* SetProtocol_Command programs the CR95HF for
ISO/IEC 15693 operation.

This requires three steps.
1. send command
2. poll to see if CR95HF has data
3. read the response

If the correct response is received the serial monitor is used
to display successful programming. 
*/
void SetProtocol_Command()
 {
 byte i = 0;

// step 1 send the command
  digitalWrite(SSPin, LOW);
  SPI.transfer(0x00);  // SPI control byte to send command to CR95HF
  SPI.transfer(0x02);  // Set protocol command
  SPI.transfer(0x02);  // length of data to follow
  SPI.transfer(0x01);  // code for ISO/IEC 15693
  SPI.transfer(0x0D);  // Wait for SOF, 10% modulation, append CRC
  digitalWrite(SSPin, HIGH);
  delay(1);

// step 2, poll for data ready

  digitalWrite(SSPin, LOW);
  while(RXBuffer[0] != 8)
    {
    RXBuffer[0] = SPI.transfer(0x03);  // Write 3 until
    RXBuffer[0] = RXBuffer[0] & 0x08;  // bit 3 is set
    }
  digitalWrite(SSPin, HIGH);
  delay(1);

// step 3, read the data
  digitalWrite(SSPin, LOW);
  SPI.transfer(0x02);   // SPI control byte for read         
  RXBuffer[0] = SPI.transfer(0);  // response code
  RXBuffer[1] = SPI.transfer(0);  // length of data
  digitalWrite(SSPin, HIGH);

  if ((RXBuffer[0] == 0) & (RXBuffer[1] == 0))
  {
     Serial.println("PROTOCOL SET-");  //
     NFCReady = 1; // NFC is ready
  }
  else
  {
     Serial.println("BAD RESPONSE TO SET PROTOCOL");
     NFCReady = 0; // NFC not ready
  }
  Serial.println(" ");
}

If you’ve received a valid response to the Set Protocol command you can move on to trying to detect RF tags in range of the BM019.  At this point we are embedding the Inventory Command from the ISO/IEC 15693 protocol into the CR95HF’s Send Receive Command.  All of that is wrapped around the extra, and kind of peculiar 3 step SPI communication process (send command, poll to see if response is ready, read response).  Here’s the code that does that.

/* Inventory_Command chekcs to see if an RF
tag is in range of the BM019.

This requires three steps.
1. send command
2. poll to see if CR95HF has data
3. read the response

If the correct response is received the serial monitor is used
to display the the RF tag's universal ID.  
*/
void Inventory_Command()
 {
 byte i = 0;

// step 1 send the command
  digitalWrite(SSPin, LOW);
  SPI.transfer(0x00);  // SPI control byte to send command to CR95HF
  SPI.transfer(0x04);  // Send Receive CR95HF command
  SPI.transfer(0x03);  // length of data that follows is 0
  SPI.transfer(0x26);  // request Flags byte
  SPI.transfer(0x01);  // Inventory Command for ISO/IEC 15693
  SPI.transfer(0x00);  // mask length for inventory command
  digitalWrite(SSPin, HIGH);
  delay(1);

// step 2, poll for data ready
// data is ready when a read byte
// has bit 3 set (ex:  B'0000 1000')

  digitalWrite(SSPin, LOW);
  while(RXBuffer[0] != 8)
    {
    RXBuffer[0] = SPI.transfer(0x03);  // Write 3 until
    RXBuffer[0] = RXBuffer[0] & 0x08;  // bit 3 is set
    }
  digitalWrite(SSPin, HIGH);
  delay(1);

// step 3, read the data
  digitalWrite(SSPin, LOW);
  SPI.transfer(0x02);   // SPI control byte for read         
  RXBuffer[0] = SPI.transfer(0);  // response code
  RXBuffer[1] = SPI.transfer(0);  // length of data
  for (i=0;i<RXBuffer[1];i++)      
      RXBuffer[i+2]=SPI.transfer(0);  // data
  digitalWrite(SSPin, HIGH);
  delay(1);

  if (RXBuffer[0] == 128)
  {  
    Serial.println("TAG DETECTED");
    Serial.print("UID: ");
    for(i=11;i>=4;i--)
    {
      Serial.print(RXBuffer[i],HEX);
      Serial.print(" ");
    }
    Serial.println(" ");
  }
  else
    {
    Serial.print("NO TAG IN RANGE - ");
    Serial.print("RESPONSE CODE: ");
    Serial.println(RXBuffer[0],HEX);
    }
  Serial.println(" ");
}

In  the main loop of my code I check to see if the protocol was set correctly (using the NFCReady variable).  If is it then I just call the Inventory command once-per-second.  If a RF tag is within range the universal ID will be displayed. If no tag is present a message showing the lack of a response and any error code will be displayed.

void loop() {

  if(NFCReady == 0)
  {
    IDN_Command();  // reads the CR95HF ID
    delay(1000);
    SetProtocol_Command(); // ISO 15693 settings
    delay(1000);
  }
  else
  {
    Inventory_Command();
    delay(1000);    
  }  

}

Here’s a glimpse of the Arduino serial monitor program as a RF tag is moved into the range of the BM019.  The response code 0x87 occurs when no tag is detected.

NFC_arduino_tag_detect

Source code for implementing the Inventory command can be found here, and code to read/write to RF tag memory is here.

Comments

  1. Duncan Hill says:

    Hi there, do you have a European distributor at all? I’d like one of these cards, but the delivery is a bit steep!

    • Hello Duncan,

      We do not currently have European distributors for our breakout modules. We have them priced very low, so there is probably not much opportunity for distributors to make money on them.

      Lon

      • Thanks. There is no chance of introducing other shipping options is there? $48 for a $16 board is not very cost effective!

  2. Olav Gullaksen says:
  3. James Dixon says:

    I’m having a bit of trouble with this. In the Serial monitor, I keep receiving that the protocol set was fine however the inventory command failed. Could this be because my CR95HF is busted? I am actually running this on a Click Board, but I am assuming because they are running the same chip, this code is still valid.

    • Hi James,

      I’m not familiar with a Click Board. Setting the protocol is an operation that interfaces to the CR95HF. Reading inventory is an operation that requires an NFC card in proximity to the CR95HF antenna.

      There could be a number of reasons why an inventory command does not come back correctly.

      1. The protocol programmed into the CR95HF is not compatible with the NFC card you are using. For starters, the Arduino code works with a 15693 type card. If you’re not using that style of card the command might not work correctly.

      2. The card is not close enough to the CR95HF antenna. Our BM019 module has a range of about 1″. I don’t know what type of antenna matching exists on the Click Board, but if you lay the card on the on the antenna that should be close enough even for a poorly matched antenna (assuming the protocol settings are correct).

      3. The CR95HF could be damaged, but since you can program the protocol it would have to be on the antenna side of things, so you should look there.

      4. The CR95HF requires specific IO’s to be at specific voltage levels to select SPI communication. When debugging an embedded control system you can sometimes have a chicken and egg situation where the controller (in this case an Arduino) sets specific IOs when you upload the program, but during initial power-up an attached device (the CR95HF) sees different values. I don’t recall if the Arduino code I wrote seeks feedback from the CR95HF to ensure the protocol has been set. It could just be sending the protocol data to the CR95HF without verifying the communication is actually taking place.

      Take a look at the CR95HF data sheet and put pull-up or pull-down resistors on the IO’s that select SPI communication. Verify that the board you’re using is not pulling these signals to a different state on power-up. For example, if the Click Board enables serial communication by default, you probably won’t be able to use SPI to control the CR95HF. Instead you’d have to use serial communication.

      Hope this helps.

      Lon

  4. I believe I work with the same click board as James Dixon. I am getting absolutely nothing useful back from the board. I mean, I am still stuck at the IDN part. Even the ECHO command doesn’t work for me. It keeps returning zero, but when I look on the logic analyzer, the MISO always sends the same thing back as the MOSI sends (when i send 1, I get 1 back. i send 3, I get 3 back).
    I have been asking everywhere, no-one has an answer. I am hoping you can help me.

    • Hi Eveline,

      I’m not familiar with the click board. Are you using the Arduino code sample I wrote?

      From a generic interface standpoint the CR95HF requires SS0 and SS1 to be set at specific levels on power up to enter SPI mode. Prior to the interface being selected the IRQ_IN pin must receive a low pulse, after which the CR95HF selects the operating interface. On the BM019 the IRQ_IN pin is labeled DIN, as it’s functionality is shared with the UART interface.

      Page 9 of the ST Micro datasheet details the start-up sequence.

      Lon

      • Hi Lon,

        Thank you for your reply.

        I am using the exact code as above (the only difference is the pin number to the IRQ-pin ).
        I added the SS0 and SS1 to the code (turning 0 on and 1 off according to the datasheet of the MCU). But nothing seems to have changed.

        I ordered a second RFid click board and I’m having the exact same problem.

        Eveline

        • Hi Evenline,

          I Googled NFC Click Board and the one I found was from Mikroelectronika. They have a surfacemount jumper shown in their schematic that connects URX to INT_I. URX is the IRQ_IN pin. You would need to send the IRQ_IN pulse at the header connection for INT_I if you have not changed the jumper.

          The CR95HF datasheet also specifies that the VPS should be 0 prior to initiating the startup sequence. I take this to mean that the start-up sequence should be executed after power-up. If you are uploading an Arduino program through their IDE that’s not the same as powering up the system.

          As a test you could insert code to light an LED inside the IDN_Command() function. Do so under this line…

          if ((RXBuffer[0] == 0) & (RXBuffer[1] == 15))
          {

          After uploading the program to the Arduino, cycle the power. If the LED lights then you know you have communicated with the CR95HF.

          Let me know if this is helpful.

          Lon

Speak Your Mind

*