Read UID
Alright, let's get to the fun part and dive into some action! We'll start by writing a simple program to read the UID of the RFID tag.
Generate project using esp-generate
We will enable async (Embassy) support for this project. To create the project, use the esp-generate
command. Run the following:
esp-generate --chip esp32 rfid-uid
This will open a screen asking you to select options.
- Select the option "Adds embassy framework support".
Just save it by pressing "s" in the keyboard.
Additional Crates required
Update your Cargo.toml to add these additional crate along with the existing dependencies.
#![allow(unused)] fn main() { mfrc522 = "0.8.0" embedded-hal-bus = "0.2.0" }
mfrc522 Driver
We will be using the awesome crate "mfrc522". It is still under development. However, it has everything what we need for purposes.
embedded-hal-bus
To understand why we need the "embedded-hal-bus" crate, we first need to understand the Embedded HAL (Hardware Abstraction Layer). Embedded HAL provides several traits that offer a standard way to control common peripherals like GPIO, PWM, and communication interfaces (such as I2C, SPI, and UART) on microcontrollers. This allows drivers to be compatible across multiple microcontrollers (e.g., ESP32, Raspberry Pi Pico).
When we want to communicate with an RFID tag using SPI, embedded-hal provides the SpiBus
and SpiDevice
traits to support bus sharing. SpiBus represents the entire bus, while SpiDevice represents a device on that bus. Microcontroller-specific HALs (e.g., esp-hal) usually implement the SpiBus trait, and device drivers like mfrc522 implement the SpiDevice trait.
So, we need to get the SpiDevice from the SpiBus to use it with the SD card. This is where the embedded-hal-bus crate helps. It provides different implementations of SpiDevice, like CriticalSectionDevice, ExclusiveDevice, and others. We'll use the ExclusiveDevice, as it's the simplest way to get an SpiDevice from an SpiBus, and it's suitable when no other devices are sharing the SPI bus.
Setting Up the SPI for the RFID Reader
To communicate with the RFID module, we will initialize the SPI instance using the SPI2 peripheral. In this setup, we will configure the SPI clock to 5MHz and map the necessary pins to GPIOs for proper communication.
#![allow(unused)] fn main() { let spi = Spi::new_with_config( peripherals.SPI2, Config { frequency: 5.MHz(), mode: SpiMode::Mode0, ..Config::default() }, ) .with_sck(peripherals.GPIO18) .with_mosi(peripherals.GPIO23) .with_miso(peripherals.GPIO19); let sd_cs = Output::new(peripherals.GPIO5, Level::High); }
Getting the SpiDevice
from SPI Bus
To work with the mfrc522 crate, we need an SpiDevice
. Since we only have the SPI bus from ESP-HAL, we'll use the embedded_hal_bus crate to get the SpiDevice
from the SPI bus.
#![allow(unused)] fn main() { let delay = Delay::new(); let spi = ExclusiveDevice::new(spi, sd_cs, delay).unwrap(); }
Initialize the mfrc522
Next, we initialize the MFRC522 driver. To do this, we wrap the SpiDevice instance with the SpiInterface wrapper provided by the mfrc522 crate and pass it to the Mfrc522 initialization:
#![allow(unused)] fn main() { let spi_interface = SpiInterface::new(spi); let mut rfid = Mfrc522::new(spi_interface).init().unwrap(); }
Helper Function to Print Byte Array as Hex String
We'll use this helper function to convert a u8 byte array (like a UID) into a printable hex string. This function will be used throughout the RFID exercises to display data. We might tweak it slightly depending on the specific requirements of each exercise.
#![allow(unused)] fn main() { fn print_hex_bytes(data: &[u8]) { for &b in data.iter() { print!("{:02x} ", b); } println!(); } }
Read the UID and Print
The main logic for reading the UID is simple. We continuously send the REQA (Request A) command to check if a card or tag is nearby. If a card is present, it responds with the ATQA (Answer To reQuest code A).
The ATQA contains information about the tag's type, capabilities, and other details. Using the ATQA response, we select the tag and retrieve its UID.
#![allow(unused)] fn main() { loop { if let Ok(atqa) = rfid.reqa() { println!("Answer To reQuest code A"); Timer::after(Duration::from_millis(50)).await; if let Ok(uid) = rfid.select(&atqa) { print_hex_bytes(uid.as_bytes()); Timer::after(Duration::from_millis(500)).await; } } } }
Clone the existing project
You can also clone (or refer) project I created and navigate to the rfid-uid
folder.
git clone https://github.com/ImplFerris/esp32-projects
cd esp32-projects/rfid-uid
Print the UID
After flashing the code onto the ESP32, bring the RFID tag close to the reader. The UID bytes will be displayed in the system console in hex format. Next, try the same with the key fob;it should display a different UID.
Full code
#![no_std] #![no_main] use embassy_executor::Spawner; use embassy_time::{Duration, Timer}; use embedded_hal_bus::spi::ExclusiveDevice; use esp_backtrace as _; use esp_hal::{ delay::Delay, gpio::{Level, Output}, prelude::*, spi::{ master::{Config, Spi}, SpiMode, }, }; use esp_println::{print, println}; use log::info; use mfrc522::{comm::blocking::spi::SpiInterface, Mfrc522}; #[main] async fn main(_spawner: Spawner) { let peripherals = esp_hal::init({ let mut config = esp_hal::Config::default(); config.cpu_clock = CpuClock::max(); config }); esp_println::logger::init_logger_from_env(); let timer0 = esp_hal::timer::timg::TimerGroup::new(peripherals.TIMG1); esp_hal_embassy::init(timer0.timer0); info!("Embassy initialized!"); let delay = Delay::new(); let spi = Spi::new_with_config( peripherals.SPI2, Config { frequency: 5.MHz(), mode: SpiMode::Mode0, ..Config::default() }, ) .with_sck(peripherals.GPIO18) .with_mosi(peripherals.GPIO23) .with_miso(peripherals.GPIO19); let sd_cs = Output::new(peripherals.GPIO5, Level::High); let spi = ExclusiveDevice::new(spi, sd_cs, delay).unwrap(); let spi_interface = SpiInterface::new(spi); let mut rfid = Mfrc522::new(spi_interface).init().unwrap(); loop { if let Ok(atqa) = rfid.reqa() { println!("Answer To reQuest code A"); Timer::after(Duration::from_millis(50)).await; if let Ok(uid) = rfid.select(&atqa) { print_hex_bytes(uid.as_bytes()); Timer::after(Duration::from_millis(500)).await; } } } } fn print_hex_bytes(data: &[u8]) { for &b in data.iter() { print!("{:02x} ", b); } println!(); }