RFID for Uno and ESP32s

This post offers an introduction to RFID (Radio Frequency ID) tags and readers.

RFID – pronounced as letters, R-F-I-D or, areFID (less common) is a technology that allows tags, fobs and cards to be read by matching readers. The tags are passive — they do not need power/battery, while readers are powered and connected you your microcontroller.

RFID is analogous to a barcode reader system — but instead of using a visual machine readable code, it uses radio transmission (a tiny radio network) to read tags. While it is technically possible with some systems to write information to tags as well, that is beyond this post.

Here I will only focus on reading tags.

Please note that different readers use different frequencies and it is important to match tags to readers — they are NOT cross-compatible.

We have two kinds of readers and tags. Keep them sorted!

ParalLax readers (5V)

We have a few older parallax readers. These operate at 125kHz and have a rectangular hole in the middle. If you look closely you can see the antenna traces around the edge of the opening.

The range on this reader is about 3in or 10cm.

We have a variety of tags for these readers. The majority look like these disks.

These readers have become ridiculously expensive and are unlikely to be replaced if broken — please use with reasonable care.

The readers are connected to an asynchronous serial port (RX,TX pins) for reading. This creates different challenges depending on which microcontroller you are using.

RC522 readers (3.3V)

We also own a number of RC522 readers. These are newer and use 13,56MHz. a much higher frequency, to communicate with tags.

These have less physical range (~3cm) compared with the parallax readers (~10cm), and connect to your micro controller using the SPI serial protocol. The wiring is very different. As of 2024, we only have the blue keyfob tags for these readers.

These higher frequency tags also store A LOT more information (about 1K) than the lower frequency tags. Most of this data is unnecessary, but it could be useful in projects that want to explore beyond reading IDs.

Connections and Code

There are several setups that arise from the various hardware we have access to. Each has a nuance that you need to be looking out for but all do basically the same thing.

Each combination is linked below:

Uno to Parallax reader

ESP to Parallax reader

UNO and ESP to RC522 reader

UNO to Parallax Reader

The quickest setup for learning RFID is to use an Arduino UNO with a PARALLAX reader. It uses serial you are familiar with and only a few connections.

Note: I do not think this example works with the black version of the reader board. We do not have black boards so not an issue for borrowed parts, but in case you go shopping…

Specific to this example:

Given the set up I have chosen, the RFID and the USB cable both talk to the main UNO chip on the same pins. Like software collisions where only one software platform can be connected to your device at a time, this can cause trouble for uploading and then RFID reading.

You will need to connect and remove the BLUE wire between UNO RX and the reader SOUT as described below.

To UPLOAD CODE:

REMOVE the connection between UNO RX (pin0) and the RFID reader"s SOUT pin

TO READ RFID:

RECONNECT RX (pin0) and the RFID reader's SOUT pin

Circuit

Remember, this reader is a 5V device.

Code

Note: multi line comments get split when copy and pasting. If you get unsolvable errors in the Arduino editor use download link at bottom of code.

//  Scissors Example:  RFID.ino
//  Scissors used to grab data from an RFID tag reader 
//  updated 2024 (hex705)

//  Match the BAUD in .begin() call to the BAUD of your reader.
//  Set DELIMITER to -1
//  Set START_BYTE and END_BYTE to match tag structure.

/* circuit 
   vcc= 5V
   gnd = gnd
   s-out of reader to pin 0 (RX arduino UNO ) - remove for upload, reconnect for read
   sn (reader) is enable it must be LOW -- connect to ground or control with pin (in this case 2)
*/

/* 
   indicators
   ironically -- a red LED is good on my reader -- a green led is bad
   -- when the enable pin is pulled LOW (down) teh LED goes red adn tags can be read
*/


// ************   NOTE -- upload challenges   **************
// in this care RFID is using UNO serial pins.
// these are connected to USB 
// !!!   you MUST disconnect the data line to UNO pin 0 to upload, then reconnect to read  !!!!
// ************


// Example developed by hex705 (Steve Daniels)
// github.com/hex705


#include <Scissors.h>

  Scissors RFID_reader;
  
  String newTag = "";
  String oldTag ="";

  int enablePin = 2; // esp 14
  
  
void setup() {
   Serial.begin(2400);

    RFID_reader.begin(Serial); 
   
   // scissors needs to have the package structure set to match RFID tags.
   // each tag send a message of form:
   // START_BYTE payload_of_length_10_bytes END_BYTE
   
     RFID_reader.setStartByte( 0x0A ); // 10 == start_byte
     RFID_reader.setEndByte  ( 0x0D ); // 13 == end_byte
     RFID_reader.setDelimiter( -1 );   // no delimiters 
   
   // all of the above could be replaced with the following
   // RFID_reader.begin(2400,10,13,-1); 

  pinMode(enablePin,OUTPUT);

  digitalWrite(enablePin,LOW); // enable LOW ! 
  Serial.println("rfid testing, setup complete");
} 


void loop () {


  RFID_reader.poll();  // check for messages

  if ( ! newTag.equals(oldTag) ) {
      Serial.println(newTag);                 // only print new tags that are different than last tag  
  } 
       
  oldTag = newTag;
       
  delay(100); // 2400 BAUD is fairly slow -- a little delay helps keep the system from bogging down
  
} 

void scissorsEvent( String &theMessage ){  // its just a string

    Serial.println("\nReceived ");
    Serial.println(RFID_reader.getRaw());

    //data extraction from incoming message
    Serial.println("parsing message");
    newTag = RFID_reader.getString(0);

}

[back to top]

ESP32 to Parallax Reader

The code for the ESP32 is little more complex than for the UNO. This is due to hardware differences between an ESP32 and the chip that makes and UNO and UNO.

One advantage of the ESP32 setup is you DO NOT have to change the circuit to upload code and change it back to read RFID (see above example).

NOTE: I do not think this example works with the black version of the reader board. We do not have black boards — but in case you go shopping…

Circuit

Remember, this reader is a 5V device. Your esp32 is 3.3V

The Vcc pin on the reader should get connect to the USB pin (5V) on the ESP32 — not to the 3.3V power rail!

Also, the /ENABLE pin is pulled low (connected to ground) to activate. In the UNO example above /ENABLE is connected to a digital pin, which is written low in setup to turn the reader on.

Both strategies work, this system does not let you disable the reader. If that is important, do not connect /ENABLE directly to ground. Rather, connect /ENABLE to a digital pin and write that pin high (disable) or low (enable)

Code
Specific to this example:

The ESP32 has multiple hardware serial options — while the UNO only has one. In almost all other examples (regardless of platform), we simply use the Serial functions set.

Serial.begin(9600);
Serial.println();
etc

This works because the Arduino environment connects Serial (code) to the USB cable on all hardware. This allows us to debug code and see variables.

On the UNO the USB connection is connected (broken out) to PINS 0, 1. So talking to hardware Serial on an UNO also communicates with these pins.

This is not the case in the ESP32.

The ESP32 has a Serial port for the USB cable (Serial) and different serial ports for devices connected to the pins of the microcontroller (Serial1, Serial2). The number suffixes matter – a lot.

This is important to understand because we will be connecting the RFID reader to one set of Serial pins for tag reading (Serial2) AND then debugging the tag value with the familiar Serial (USB) interface.

To use the UART pin interface on an ESP32 you must explicitly define the pins for RX (receive, listen) and TX (transmit, speak).

#define RXD2 16
#define TXD2 17

You must also start the Serial2 port with the line:

Serial2.begin(2400, SERIAL_8N1, RXD2, TXD2);

This sets teh speed — 2400 BAUD – to the speed of the reader. Sets up the bit pattern (SERAIL_8N1) and identifies the correct pins to use.

Finally, to get the data from the RFID reader we connect scissors to Serial2, not Serial

RFID_reader.begin(Serial2);

Full Example

Comments may cut and paste oddly — if you get errors, check that multiline comments did not get split – zip code at bottom.

//  Scissors Example:  RFID.ino
//  Scissors used as an RFID tag reader 
//  updated 2024 (hex705)

//  Match the BAUD in .begin() call to the BAUD of your reader.
//  Set DELIMITER to -1
//  Set START_BYTE and END_BYTE to match tag structure.

/* circuit 
   vcc= 5V
   gnd = gnd
   s-out of reader to pin 0 (RX arduino UNO )
   sn (reader) is enable it must be LOW -- connect  to ground or control with pin (in this case 2)
*/

/* 
   indicators
   ironically -- a red LED is good on my reader -- a green led is bad
   -- when the enable pin is pulled LOW (down) teh LED goes red adn tags can be read
*/


// ************   NOTE -- upload challenges   **************
// in this care RFID is using UNO serial pins.
// these are connected to USB 
// !!!   you MUST disconnect the data line to UNO pin 0 to upload, then reconnect to read  !!!!
// ************


// Example developed by hex705 (Steve Daniels)
// github.com/hex705


#include <Scissors.h>

// https://www.luisllamas.es/en/esp32-uart/
// define pins for serial 2 -- not the USB cable which is JUST Serial
#define RXD2 16
#define TXD2 17

Scissors RFID_reader;
  
String newTag = "";
String oldTag ="";

int enablePin = 14;
  
  
void setup() {

  Serial.begin(9600);

  // this is the serial UART (port) for the RFID reader - Serial2 -- the 2 is important!
  Serial2.begin(2400, SERIAL_8N1, RXD2, TXD2);
  // attache scissors to the stream from RFID
  RFID_reader.begin(Serial2); 
   
   // scissors needs to have the package structure set to match RFID tags.
   // each tag send a message of form:
   // START_BYTE payload_of_length_10_bytes END_BYTE
     RFID_reader.setStartByte( 0x0A ); // 10 == start_byte
     RFID_reader.setEndByte  ( 0x0D ); // 13 == end_byte
     RFID_reader.setDelimiter( -1 );   // no delimiters 
   
   // The Scissors settings above could be replaced with the following
   // RFID_reader.begin(2400,10,13,-1); 

  pinMode(enablePin,OUTPUT);

  digitalWrite(enablePin,LOW); // enable LOW ! 
  Serial.println("rfid testing, setup complete");
} 


void loop () {

  RFID_reader.poll();  // check for messages

  if ( ! newTag.equals(oldTag) ) {
      Serial.println(newTag);                 // only print new tags that are different than last tag  
  } 
       
  oldTag = newTag;
       
  delay(100); // 2400 BAUD is fairly slow -- a little delay helps keep the system from bogging down
  
} 

void scissorsEvent( String &theMessage ){  // its just a string

    Serial.println("\nReceived ");
    Serial.println(RFID_reader.getRaw());

    //data extraction from incoming message
    Serial.println("parsing message");
    newTag = RFID_reader.getString(0);

}

[back to top]

Using the RC522 RFID reader

This connection uses the SPI interface. SPI is a synchronous serial interface. This means both devices are synchronized with each other through a clock pin.

Naming the Pins – racism’s legacy

There is historically problematic language, due to racism, privilege and white supremacy, in the naming of the signal pins with this protocol. I could ask you to just follow the picture below and ignore it, but I don’t think that is the correct path.

Historically there was a dominance relationship and metaphor (master and slave) used to describe two devices connected with SPI. There is lots written about this — and you can learn more by googling the old terms if you want a fuller understanding.

While I will use new label metaphors, images and pin numbers where possible, you will see TONS of examples and even physical parts labelled using the old terms. This includes many examples attached to Arduino – and this post. As the awareness and transition are relatively recent (2020), you will encounter old labels often.

Our field is riddled with terms arising from privileged communities creating language around new technologies. We should know the history and use the more inclusive terms where possible.

New Pin Labels

The official naming protocols for SPI uses a peripheral and controller metaphor (think star topology) to indicate who is talking and who is listening. This language is excellent — but the associated labels (PICO/POCI) do not match our hardware.

Our RC522 readers use the old labels identified above. It is important that we think about the metaphors we use. It is important that we leave problematic language behind.

In this post I am going to use the wiki language and metaphor for SPI pins. In this convention M stands for MAIN, and S stands for SUB (potentially problematic in other ways). It is a compromise.

M = main

S = sub

The labels on hardware are :

MOSI (pronounced mossy) -- main out, sub in
MISO (pronounced meezo) -- main in, sub out

This is not perfect but when you look up pinouts for various devices, it will match.

Circuit – UNO

At least one user has reported that putting the 522 onto a breadboard causes reader errors or fails (https://www.youtube.com/watch?v=pdBrvLGH0PE) .

I have only set these up without a breadboard — YMMV.


Circuit – ESP32 (huzzah)
Specifics to this example:
Software library

You need to install an MFRC522 library to get this to work. I am using the library:

MFRC522 by github community

It can be installed through the Arduino library interface.

Code

The code for the two micro-contollers is almost identical. There is just a small change that is needed to switch between UNO and ESP32.

You need to tell the UNO or ESP where to find the chip select (SS) and reset pins. These are found in the example below at lines 49-55.

You should uncomment the lines that match the microcontroller you are using. And comment-out the lines for the other.

Uno
// UNO settings - comment out the ESP below
#define SS_PIN 10 // (uno)
#define RST_PIN 9 // (uno)
ESP32
// ESP settings - comment out the UNO above
#define SS_PIN 23  // (esp)
#define RST_PIN 21 // (esp)
Full example

Comments may cut and paste oddly — if you get errors, check that multiline comments did not get split – zip code at bottom.

/*

https://github.com/miguelbalboa/rfid/blob/master/src/MFRC522.h


 * --------------------------------------------------------------------------------------------------------------------
 * Example sketch/program showing how to read new NUID from a PICC to serial.
 * --------------------------------------------------------------------------------------------------------------------
 * This is a MFRC522 library example; for further details and other examples see: https://github.com/miguelbalboa/rfid
 * 
 * Example sketch/program showing how to the read data from a PICC (that is: a RFID Tag or Card) using a MFRC522 based RFID
 * Reader on the Arduino SPI interface.
 * 
 * When the Arduino and the MFRC522 module are connected (see the pin layout below), load this sketch into Arduino IDE
 * then verify/compile and upload it. To see the output: use Tools, Serial Monitor of the IDE (hit Ctrl+Shft+M). When
 * you present a PICC (that is: a RFID Tag or Card) at reading distance of the MFRC522 Reader/PCD, the serial output
 * will show the type, and the NUID if a new card has been detected. Note: you may see "Timeout in communication" messages
 * when removing the PICC from reading distance too early.
 * 
 * @license Released into the public domain.
 * 
 * Typical pin layout used:
 * -----------------------------------------------------------------------------------------
 *             MFRC522      Arduino       Arduino   Arduino    Arduino          Arduino
 *             Reader/PCD   Uno/101       Mega      Nano v3    Leonardo/Micro   Pro Micro
 * Signal      Pin          Pin           Pin       Pin        Pin              Pin
 * -----------------------------------------------------------------------------------------
 * RST/Reset   RST          9             5         D9         RESET/ICSP-5     RST
 * SPI SS      SDA(SS)      10            53        D10        10               10
 * SPI MOSI    MOSI         11 / ICSP-4   51        D11        ICSP-4           16
 * SPI MISO    MISO         12 / ICSP-1   50        D12        ICSP-1           14
 * SPI SCK     SCK          13 / ICSP-3   52        D13        ICSP-3           15
 *
 * More pin layouts for other boards can be found here: https://github.com/miguelbalboa/rfid#pin-layout
 */

 /* for esp32 huzzah 32 from adafruit - tested 2024
 * works with huzzah
 * RST/Reset   RST          21           
 * SPI SS      SDA(SS)      23           
 * SPI MOSI    MOSI         18 
 * SPI MISO    MISO         19 
 * SPI SCK     SCK          5   
 */

#include <SPI.h>
#include <MFRC522.h>

// UNO settings - comment out the ESP below
#define SS_PIN 10 // (uno)
#define RST_PIN 9 // (uno)

// ESP settings - comment out the UNO above
//#define SS_PIN 23  // (esp)
//#define RST_PIN 21 // (esp)
 
MFRC522 rfid(SS_PIN, RST_PIN); // Instance of the class


// kind of tag the 522 can read -- code for this commented out -- but just in case ... 
MFRC522::PICC_Type compatableTagType = MFRC522::PICC_TYPE_MIFARE_1K;

// string to store incoming tag ID
String newTagID = "";
String oldTagID = "";

void setup() { 
  Serial.begin(9600);
  SPI.begin(); // Init SPI bus
  rfid.PCD_Init(); // Init MFRC522 
}
 
void loop() {

  readRC522(); // check the reader

  oldTagID = newTagID;
  newTagID = "";
}

void readRC522(){

  // Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle.
  if ( ! rfid.PICC_IsNewCardPresent())
    return;

  // Verify if the NUID has been read
  if ( ! rfid.PICC_ReadCardSerial())
    return;

  // // Check t osee if tag is the Classic MIFARE type (1k)
  // Serial.print("Tag read, type: ");
  // MFRC522::PICC_Type piccType = rfid.PICC_GetType(rfid.uid.sak);
  // Serial.println(rfid.PICC_GetTypeName(piccType));

  // if (piccType != compatableTagType) {
  //   Serial.println(F("Your tag is not compatable with this reader"));
  //   return;
  // }

    // place new ID into a string for comparison and use 
    for (byte i = 0; i < 4; i++) {
     // nuidPICC[i] = rfid.uid.uidByte[i];
      newTagID+=rfid.uid.uidByte[i];
    }

  if (oldTagID != newTagID) {
    Serial.println(F("A new tag has been detected."));
    Serial.println(newTagID);
    Serial.println();
  }
  else {
    Serial.println(F("Card read previously."));
  }

  // Halt PICC
  rfid.PICC_HaltA();

  // Stop encryption on PCD
  rfid.PCD_StopCrypto1();

} // end read 

[back to top]