This experiment focuses on reading RFID tags using a Raspberry Pi Pico microcontroller connected to an RFID reader. RFID (Radio Frequency Identification) technology allows for the wireless transfer of data between a reader and a tag, enabling applications such as inventory management, access control, and object tracking. The goal of this experiment is to interface the Pico with an RFID reader, read the tag data, and display it on a console.
Scope:
The scope of this experiment includes:
Understanding the principles of RFID technology.
Setting up hardware connections between the Pico and RFID reader.
Implementing the communication protocol (SPI/I2C) for data exchange.
Writing and testing application code in both MicroPython and Embedded C.
Displaying the read tag data on the serial console.
Prerequisite:
3.1. Hardware:
Pico microcontroller
RFID reader
RFID Tag
Breadboard
Jumper wires
USB cable (to connect the computer)
3 .2. Software:
Thonny Software(For micro python)
MicroPython firmware installed on Raspberry Pi Pico
ubuntu Terminal(For embedded C)
CMake,Pico SDK,GNU Arm Embedded Toolchain,WSL.
Connection Details:
Raspberry Pi Pico | MFRC522 RFID module |
GND | GND |
3V3 | 3.3V |
GP5 | SDA |
GP6 | SCK |
GP7 | MOSI |
GP4 | MISO |
— | IRQ |
GPIO22 | RST |
Then connect the RGB LED Module R, G & B Pins to GP0, GP1 & GP2 of Raspberry Pi Pico.

Working Principle:
RFID technology works on the principle of electromagnetic fields to transfer data between the reader and the tag. The RFID reader emits a signal, which powers the passive RFID tag. The tag then transmits its stored data back to the reader. The microcontroller processes this data and displays it accordingly.
SPI (Serial Peripheral Interface) Overview
SPI (Serial Peripheral Interface) is a synchronous serial communication protocol primarily used for short-distance communication, mainly in embedded systems, sensors, and microcontrollers. It allows a microcontroller (or master device) to communicate with one or more peripheral devices (slave devices). SPI is widely used due to its simplicity, high-speed data transfer capabilities, and low power consumption.
Key Features of SPI:
Full-Duplex Communication: Data can be sent and received simultaneously.
Master-Slave Architecture: There is always one master and one or more slaves.
Synchronous Communication: It uses a shared clock line, meaning both devices use the same clock for data transmission.
High-Speed Communication: SPI can achieve high data transfer speeds, making it ideal for communication with high-speed devices like displays, sensors, and SD cards.
Flexible Data Size: It allows the transmission of data in 8-bit, 16-bit, or other sizes, depending on the configuration.
SPI Signals and Pins
SPI uses four main signal lines:
MISO (Master In Slave Out):
This line is used to transmit data from the slave to the master.
Data is output from the slave and input to the master.
MOSI (Master Out Slave In):
This line is used to transmit data from the master to the slave.
Data is output from the master and input to the slave.
SCK (Serial Clock):
This clock signal is generated by the master and shared with the slave(s).
It synchronizes the data transmission.
SS (Slave Select) (also called CS – Chip Select):
This signal is used to select a particular slave device for communication.
When SS is low (active), the slave listens to the master, and when SS is high (inactive), the slave ignores communication.
In multi-slave systems, each slave has its own SS/CS pin.
SPI Communication Modes
The SPI communication can operate in four different modes, determined by two parameters: Clock Polarity (CPOL) and Clock Phase (CPHA).
CPOL: Defines whether the clock is idle high (1) or idle low (0).
CPHA: Defines when data is sampled, either on the leading edge or the trailing edge of the clock pulse.
The combinations of CPOL and CPHA give four possible SPI modes:
Mode 0: CPOL = 0, CPHA = 0 (Clock idle low, data captured on rising edge)
Mode 1: CPOL = 0, CPHA = 1 (Clock idle low, data captured on falling edge)
Mode 2: CPOL = 1, CPHA = 0 (Clock idle high, data captured on falling edge)
Mode 3: CPOL = 1, CPHA = 1 (Clock idle high, data captured on rising edge)
These modes ensure flexibility in how the master and slave communicate, depending on device specifications.
SPI in Master-Slave Configuration
Master: The device that controls the clock signal (SCK) and manages communication.
Slave: The device that responds to the master and sends/receives data when selected via SS/CS.
Multiple Slave Communication
SPI supports multiple slaves through multiple SS/CS lines. The master can select any one slave at a time by pulling its SS/CS line low, while keeping others high (inactive).
SPI Communication Steps
Master starts communication: The master pulls the SS/CS line of the desired slave low.
Clock pulses generated: The master generates the clock signal, which is shared with the slave.
Data exchange:
The master sends data on the MOSI line, which the slave reads.
Simultaneously, the slave can send data on the MISO line, which the master reads.
Communication ends: Once the data exchange is complete, the master pulls the SS/CS line of the slave high to stop communication.
RFID?
RFID is an acronym for Radio Frequency Identification which means RFID is the wireless, non-contact use of radio frequency waves to transfer data and identify objects, animals, or humans. RFID systems are usually comprised of an RFID reader, RFID tags, and antennas. RFID is widely used in industries like healthcare, retail, hospitality, and manufacturing - just to name a few. RFID is just like barcodes but is not restricted by line-of-sight.
How Does RFID Work?
Tagging items with RFID tags allows users to automatically and uniquely identify and track inventory and assets. RFID uses radio waves sent via an RFID antenna to RFID tags in the surrounding area. RFID readers amplify energy, modulate it with data, and send the energy at a certain frequency out to an RFID antenna cable to the connected RFID antenna
The ability to identify each individual RFID tag being read is all thanks to a unique identifier (unique information) in the RFID tag’s memory. This unique identifier enables two physically identical items to be easily distinguished from one another by a simple read.
RFID takes auto-ID technology to the next level by allowing tags to be read without line of sight and up to 30+ meters away.
RFID has come a long way from its first application of identifying airplanes as friend or foe in World War II back in the 1930s. Not only does the technology continue to improve year over year, but the cost of implementing and using an RFID system continues to decrease, making RFID more cost-effective and efficient. For more information about how RFID is continuing to improve, check out our article - RFID Failed You in the Past? It May Have Improved More Than You Think.
RFID Used For?
Supply Chain Management
Inventory Tracking
Tolling
Hospital Infant Tracking
Pipe and Spool Tracking
Logistics Tracking (Materials Management)
DVD Kiosks
Library Materials Tracking
Real-Time Location Systems
Types of RFID Frequencies
When learning about RFID technology, it is important to note that there are three main areas or frequencies that all have different read ranges and specifications.Within the Electromagnetic Spectrum, there are three primary frequency ranges used for RFID transmissions – Low Frequency RFID (LF RFID), High Frequency RFID (HF RFID), and Ultra-High Frequency RFID (UHF RFID).

Low Frequency

General Frequency Range: 30 - 300 kHz
Primary Frequency Range: 125 - 134 kHz
LF RFID Read Range: Contact - 10 Centimeters
Average Cost Per LF RFID Tag: $0.75 - $5.00
Applications: Animal Tracking, Access Control, Car Key-Fob, Applications with High Volumes of Liquids and Metals
Pros: Works well near Liquids & Metals, Global Standards
Cons: Very Short Read Range, Limited Quantity of Memory, Low Data Transmission Rate, High Production Cost
High Frequency

Primary Frequency Range: 13.56 MHz
HF RFID Read Range: Near Contact - 30 Centimeters
Average Cost Per HF RFID Tag: $0.20 - $10.00
Applications: DVD Kiosks, Library Books, Personal ID Cards, Poker/Gaming Chips, NFC Applications
Pros: NFC Global Protocols, Larger Memory Options, Global Standards
Cons: Short Read Range, Low Data Transmission Rate
Ultra-High Frequency

General Frequency Range: 300 - 3000 MHz
Primary Frequency Ranges: 433 MHz, 860 - 960 MHz
There are two types of RFID that reside within the Ultra High Frequency range: Active RFID and Passive RFID.
What is Active RFID?
Primary Frequency Range: 433 MHz, (Can use 2.45 GHz - under the Extremely High-Frequency Range)
Active RFID Read Range: 30 - 100+ Meters
Average Cost Per Active RFID Tag: $15.00 - $50.00
Applications: Vehicle Tracking, Auto Manufacturing, Mining, Construction, Asset Tracking, Cargo Container Tracking, Construction Tools
Pros: Very Long Read Range, Lower Infrastructure Cost (vs. Passive RFID), Large Memory Capacity, High Data Transmission Rates
Cons: High Per Tag Cost, Shipping Restrictions (due to batteries), Complex Software may be Required, High Interference from Metal and Liquids; Few Global Standards
What is Passive RFID?
Primary Frequency Ranges: 860 - 960 MHz
Passive RFID Read Range: Near Contact - 25 Meters
Average Cost Per UHF RFID Tag: $0.08 - $20.00
Applications: Manufacturing, Pharmaceuticals, Tolling, Inventory Tracking, Race Timing, Asset Tracking, Supply Chain Management, IT Asset Tracking, Tool Tracking, Laundry Tracking, Library Management, Access Control, User Experience
Pros: Long Read Range, Low Cost Per Tag, Wide Variety of Tag Sizes and Shapes, Global Standards, High Data Transmission Rates
Cons: High Equipment Costs, Moderate Memory Capacity, High Interference from Metal and Liquids
RFID System?
While each system will vary in terms of device types and complexity, traditional (fixed) RFID systems contain at least the following four components:
Readers
Antennas
Tags
Cables
The exception to that rule is when a system uses a mobile/handheld/USB reader or other integrated reader which combines the reader, antenna, and cabling.
A mobile handheld RFID reader (with an integrated antenna) and RFID tags make up the simplest RFID system, while more complex systems are designed using multi-port readers, antenna hubs (https://www.atlasrfidstore.com/rfid-insider/antenna-multiplexers-save-thousands-of-dollars/), GPIO boxes, additional functionality devices (e.g. stack lights), multiple antennas and cables, RFID tags, and a complete software setup.

An RFID tag in its most simplistic form, is comprised of two parts – an antenna for transmitting and receiving signals, and an RFID chip (or integrated circuit, IC) which stores the tag’s ID and other information. RFID tags are affixed to items in order to track them using an RFID reader and antenna.
RFID tags transmit data about an item (contained on the RFID chip) through radio waves to the antenna/reader combination. RFID tags typically do not have a battery (unless specified as Active or BAP tags); instead, they receive energy from the radio waves generated by the reader. When the RFID tag receives the transmission from the reader/antenna, the energy runs through the internal antenna to the tag’s chip. The energy activates the RFID chip, which modulates the energy with the desired information, and then transmits a signal back toward the antenna/reader.
On each RFID chip, there are four memory banks – EPC, TID, User, and Reserved. Each of these memory banks contains information about the item that is tagged or the tag itself depending on the bank and what has been specified. Two of the four memory banks, the EPC and User, can be programmed with a unique number or information for identifying the item on which it’s placed. The TID bank cannot be updated because it contains information about the tag itself as well as the unique tag identifier. The RFID tag’s Reserved memory bank is used for special tag operations, like locking the tag or expanding its available EPC memory.
Hundreds of different RFID tags are available in many shapes and sizes with features and options specific to certain environments, surface materials, and applications. It’s important to choose the ideal tag for the specific application, environment, and item material in order to get the best performance out of your tag. To learn more about how to choose RFID tags for your application and then how to test them
What is an RFID Reader?

An RFID reader is the brain of the RFID system and is necessary for any system to function. Readers, also called interrogators, are devices that transmit and receive radio waves in order to communicate with RFID tags. RFID readers are typically divided into three distinct types in terms of mobility/flexibility – Fixed RFID Readers, Mobile RFID Readers, and USB Readers.
Fixed readers stay in one location and are typically mounted on walls, on desks, into portals, or in other stationary locations. Fixed RFID Readers typically have external antenna ports that can connect anywhere from one additional antenna to up to eight different antennas. With the addition of one or more multiplexers, some readers can connect to up to 64 RFID antennas. The number of antennas connected to one reader depends on the area of coverage required for the RFID application. Some desktop applications, like checking files in and out, only need a small area of coverage, so one antenna works well. Other applications with a larger area of coverage, such as a finish line in a race timing application typically require multiple antennas to create the necessary coverage zone.
Mobile readers are handheld devices that allow for flexibility when reading RFID tags while still being able to communicate with a host computer or smart device. Most mobile devices are cordless and rely on a battery for a power source and Wi-Fi or Bluetooth for data transmission. There are two primary categories of Mobile RFID readers – readers with an onboard computer, called Mobile Computing Devices, and readers that use a Bluetooth or Auxiliary connection to a smart device or tablet, called Sleds.
A common subset of fixed or mobile readers is integrated readers. Most mobile readers are integrated readers, but fixed readers are available as lone devices or as integrated devices. An integrated RFID reader is a reader with a built-in antenna that does not have to be connected to an external antenna. Integrated readers are usually aesthetically pleasing and designed to be used for indoor applications without a high traffic of tagged items.
USB Readers are a unique subset of RFID readers because, while they are fixed to a computer, they are not fixed to a wall outlet, allowing them to have more mobility than a typical fixed RFID reader. USB readers are incredibly popular for any desktop applications or specifically for reading and writing individual RFID tags.
RFID RC522 Module
The RC522 RFID module based on the MFRC522 IC from NXP Semiconductor. It usually comes with an RFID card tag and a key tag. It has 1KB of memory and it is used to write a tag. Any message can be stored using this memory.
The RC522 RFID reader module is designed to create a 13.56MHz electromagnetic field and communicate with RFID tags. The reader can communicate with any microcontroller over a 4-pin SPI. In SPI Mode it can communicate with a maximum data rate of 10 Mbps. It also supports communication over I2C and UART protocols.
The RC522 RFID module can be programmed to generate an interrupt, allowing the module to alert us when a tag approaches it. The module’s operating voltage ranges from 2.5 to 3.3V, but its SPI Pin is 5V tolerant. Therefore you can connect the RC522 Module to Arduino or Raspberry Pi Pico.
Specifications
Module interfaces SPI: Data transfer rate maximum 10 Mbit/s
Power Voltage: 2.5-3.3 V
Operating frequency: 13.56 MHz
Operating current: 13-26 mA/DC 3.3 V
Idle current: 10-13 mA/DC 3.3 V
Sleep current: <80 uA
Peak current: <30 mA
Read Range: 0 ~ 35 mm (mifare1 card)
Supported card types: Mifare1 S50, Mifare1 S70, MIFARE Ultralight, Mifare Pro, and MIFARE DESFire
RFID RC522 Pinout
The RFID RC522 communicates via SPI, I2C & UART Communication Mode. It has a total number of 9 pins.
SDA SCL: I2C Communication pins. DATA and CLOCK.
SS SCK MOSI MISO: SPI communication pins. Slave Select, Clock, MOSI, and MISO.
RX TX: UART Communication pins.
IRQ: Interrupt signal from the module to indicate RFID tag detection.
GND: Ground pin that needs to be connected to the GND pin on the Arduino.
RST: Reset pin for the module
VCC: Supply pin for the module (2.5V to 3.3V).
Application code:
MicroPython:
from mfrc522 import MFRC522
from machine import Pin
import utime
# Initialize the MFRC522 reader
reader = MFRC522(spi_id=0, sck=6, miso=4, mosi=7, cs=5, rst=22)
# Initialize LED pins
red = Pin(0, Pin.OUT)
green = Pin(1, Pin.OUT)
blue = Pin(2, Pin.OUT)
# Turn off all LEDs initially
red.value(0)
green.value(0)
blue.value(0)
print("Bring TAG closer...")
print("")
while True:
# Initialize RFID reader for every loop iteration
reader.init()
(stat, tag_type) = reader.request(reader.REQIDL)
if stat == reader.OK:
(stat, uid) = reader.SelectTagSN()
if stat == reader.OK:
# Convert the UID to an integer
card = int.from_bytes(bytes(uid), "little", False)
print("CARD ID: " + str(card))
# Turn off all LEDs first
red.value(0)
green.value(0)
blue.value(0)
# Control LEDs based on the card ID
if card == 4120449875:
green.value(1)
elif card == 3310611155:
blue.value(1)
else:
red.value(1) # If card is unrecognized, turn on red LED
# Sleep to avoid continuous polling
utime.sleep_ms(500)
MPRC522 LIBRARY:
from machine import Pin, SPI
from os import uname
class MFRC522:
DEBUG = False
OK = 0
NOTAGERR = 1
ERR = 2
REQIDL = 0x26
REQALL = 0x52
AUTHENT1A = 0x60
AUTHENT1B = 0x61
PICC_ANTICOLL1 = 0x93
PICC_ANTICOLL2 = 0x95
PICC_ANTICOLL3 = 0x97
def __init__(self, sck, mosi, miso, rst, cs,baudrate=1000000,spi_id=0):
self.sck = Pin(sck, Pin.OUT)
self.mosi = Pin(mosi, Pin.OUT)
self.miso = Pin(miso)
self.rst = Pin(rst, Pin.OUT)
self.cs = Pin(cs, Pin.OUT)
self.rst.value(0)
self.cs.value(1)
board = uname()[0]
if board == 'WiPy' or board == 'LoPy' or board == 'FiPy':
self.spi = SPI(0)
self.spi.init(SPI.MASTER, baudrate=1000000, pins=(self.sck, self.mosi, self.miso))
elif (board == 'esp8266') or (board == 'esp32'):
self.spi = SPI(baudrate=100000, polarity=0, phase=0, sck=self.sck, mosi=self.mosi, miso=self.miso)
self.spi.init()
elif board == 'rp2':
self.spi = SPI(spi_id,baudrate=baudrate,sck=self.sck, mosi= self.mosi, miso= self.miso)
else:
raise RuntimeError("Unsupported platform")
self.rst.value(1)
self.init()
def _wreg(self, reg, val):
self.cs.value(0)
self.spi.write(b'%c' % int(0xff & ((reg << 1) & 0x7e)))
self.spi.write(b'%c' % int(0xff & val))
self.cs.value(1)
def _rreg(self, reg):
self.cs.value(0)
self.spi.write(b'%c' % int(0xff & (((reg << 1) & 0x7e) | 0x80)))
val = self.spi.read(1)
self.cs.value(1)
return val[0]
def _sflags(self, reg, mask):
self._wreg(reg, self._rreg(reg) | mask)
def _cflags(self, reg, mask):
self._wreg(reg, self._rreg(reg) & (~mask))
def _tocard(self, cmd, send):
recv = []
bits = irq_en = wait_irq = n = 0
stat = self.ERR
if cmd == 0x0E:
irq_en = 0x12
wait_irq = 0x10
elif cmd == 0x0C:
irq_en = 0x77
wait_irq = 0x30
self._wreg(0x02, irq_en | 0x80)
self._cflags(0x04, 0x80)
self._sflags(0x0A, 0x80)
self._wreg(0x01, 0x00)
for c in send:
self._wreg(0x09, c)
self._wreg(0x01, cmd)
if cmd == 0x0C:
self._sflags(0x0D, 0x80)
i = 2000
while True:
n = self._rreg(0x04)
i -= 1
if ~((i != 0) and ~(n & 0x01) and ~(n & wait_irq)):
break
self._cflags(0x0D, 0x80)
if i:
if (self._rreg(0x06) & 0x1B) == 0x00:
stat = self.OK
if n & irq_en & 0x01:
stat = self.NOTAGERR
elif cmd == 0x0C:
n = self._rreg(0x0A)
lbits = self._rreg(0x0C) & 0x07
if lbits != 0:
bits = (n - 1) * 8 + lbits
else:
bits = n * 8
if n == 0:
n = 1
elif n > 16:
n = 16
for _ in range(n):
recv.append(self._rreg(0x09))
else:
stat = self.ERR
return stat, recv, bits
def _crc(self, data):
self._cflags(0x05, 0x04)
self._sflags(0x0A, 0x80)
for c in data:
self._wreg(0x09, c)
self._wreg(0x01, 0x03)
i = 0xFF
while True:
n = self._rreg(0x05)
i -= 1
if not ((i != 0) and not (n & 0x04)):
break
return [self._rreg(0x22), self._rreg(0x21)]
def init(self):
self.reset()
self._wreg(0x2A, 0x8D)
self._wreg(0x2B, 0x3E)
self._wreg(0x2D, 30)
self._wreg(0x2C, 0)
self._wreg(0x15, 0x40)
self._wreg(0x11, 0x3D)
self.antenna_on()
def reset(self):
self._wreg(0x01, 0x0F)
def antenna_on(self, on=True):
if on and ~(self._rreg(0x14) & 0x03):
self._sflags(0x14, 0x03)
else:
self._cflags(0x14, 0x03)
def request(self, mode):
self._wreg(0x0D, 0x07)
(stat, recv, bits) = self._tocard(0x0C, [mode])
if (stat != self.OK) | (bits != 0x10):
stat = self.ERR
return stat, bits
def anticoll(self,anticolN):
ser_chk = 0
ser = [anticolN, 0x20]
self._wreg(0x0D, 0x00)
(stat, recv, bits) = self._tocard(0x0C, ser)
if stat == self.OK:
if len(recv) == 5:
for i in range(4):
ser_chk = ser_chk ^ recv[i]
if ser_chk != recv[4]:
stat = self.ERR
else:
stat = self.ERR
return stat, recv
def PcdSelect(self, serNum,anticolN):
backData = []
buf = []
buf.append(anticolN)
buf.append(0x70)
#i = 0
###xorsum=0;
for i in serNum:
buf.append(i)
#while i<5:
# buf.append(serNum[i])
# i = i + 1
pOut = self._crc(buf)
buf.append(pOut[0])
buf.append(pOut[1])
(status, backData, backLen) = self._tocard( 0x0C, buf)
if (status == self.OK) and (backLen == 0x18):
return 1
else:
return 0
def SelectTag(self, uid):
byte5 = 0
#(status,puid)= self.anticoll(self.PICC_ANTICOLL1)
#print("uid",uid,"puid",puid)
for i in uid:
byte5 = byte5 ^ i
puid = uid + [byte5]
if self.PcdSelect(puid,self.PICC_ANTICOLL1) == 0:
return (self.ERR,[])
return (self.OK , uid)
def tohexstring(self,v):
s="["
for i in v:
if i != v[0]:
s = s+ ", "
s=s+ "0x{:02X}".format(i)
s= s+ "]"
return s
def SelectTagSN(self):
valid_uid=[]
(status,uid)= self.anticoll(self.PICC_ANTICOLL1)
#print("Select Tag 1:",self.tohexstring(uid))
if status != self.OK:
return (self.ERR,[])
if self.DEBUG: print("anticol(1) {}".format(uid))
if self.PcdSelect(uid,self.PICC_ANTICOLL1) == 0:
return (self.ERR,[])
if self.DEBUG: print("pcdSelect(1) {}".format(uid))
#check if first byte is 0x88
if uid[0] == 0x88 :
#ok we have another type of card
valid_uid.extend(uid[1:4])
(status,uid)=self.anticoll(self.PICC_ANTICOLL2)
#print("Select Tag 2:",self.tohexstring(uid))
if status != self.OK:
return (self.ERR,[])
if self.DEBUG: print("Anticol(2) {}".format(uid))
rtn = self.PcdSelect(uid,self.PICC_ANTICOLL2)
if self.DEBUG: print("pcdSelect(2) return={} uid={}".format(rtn,uid))
if rtn == 0:
return (self.ERR,[])
if self.DEBUG: print("PcdSelect2() {}".format(uid))
#now check again if uid[0] is 0x88
if uid[0] == 0x88 :
valid_uid.extend(uid[1:4])
(status , uid) = self.anticoll(self.PICC_ANTICOLL3)
#print("Select Tag 3:",self.tohexstring(uid))
if status != self.OK:
return (self.ERR,[])
if self.DEBUG: print("Anticol(3) {}".format(uid))
if self.MFRC522_PcdSelect(uid,self.PICC_ANTICOLL3) == 0:
return (self.ERR,[])
if self.DEBUG: print("PcdSelect(3) {}".format(uid))
valid_uid.extend(uid[0:5])
# if we are here than the uid is ok
# let's remove the last BYTE whic is the XOR sum
return (self.OK , valid_uid[:len(valid_uid)-1])
#return (self.OK , valid_uid)
def auth(self, mode, addr, sect, ser):
return self._tocard(0x0E, [mode, addr] + sect + ser[:4])[0]
def authKeys(self,uid,addr,keyA=None, keyB=None):
status = self.ERR
if keyA is not None:
status = self.auth(self.AUTHENT1A, addr, keyA, uid)
elif keyB is not None:
status = self.auth(self.AUTHENT1B, addr, keyB, uid)
return status
def stop_crypto1(self):
self._cflags(0x08, 0x08)
def read(self, addr):
data = [0x30, addr]
data += self._crc(data)
(stat, recv, _) = self._tocard(0x0C, data)
return stat, recv
def write(self, addr, data):
buf = [0xA0, addr]
buf += self._crc(buf)
(stat, recv, bits) = self._tocard(0x0C, buf)
if not (stat == self.OK) or not (bits == 4) or not ((recv[0] & 0x0F) == 0x0A):
stat = self.ERR
else:
buf = []
for i in range(16):
buf.append(data[i])
buf += self._crc(buf)
(stat, recv, bits) = self._tocard(0x0C, buf)
if not (stat == self.OK) or not (bits == 4) or not ((recv[0] & 0x0F) == 0x0A):
stat = self.ERR
return stat
def writeSectorBlock(self,uid, sector, block, data, keyA=None, keyB = None):
absoluteBlock = sector * 4 + (block % 4)
if absoluteBlock > 63 :
return self.ERR
if len(data) != 16:
return self.ERR
if self.authKeys(uid,absoluteBlock,keyA,keyB) != self.ERR :
return self.write(absoluteBlock, data)
return self.ERR
def readSectorBlock(self,uid ,sector, block, keyA=None, keyB = None):
absoluteBlock = sector * 4 + (block % 4)
if absoluteBlock > 63 :
return self.ERR, None
if self.authKeys(uid,absoluteBlock,keyA,keyB) != self.ERR :
return self.read(absoluteBlock)
return self.ERR, None
def MFRC522_DumpClassic1K(self,uid, Start=0, End=64, keyA=None, keyB=None):
for absoluteBlock in range(Start,End):
status = self.authKeys(uid,absoluteBlock,keyA,keyB)
# Check if authenticated
print("{:02d} S{:02d} B{:1d}: ".format(absoluteBlock, absoluteBlock//4 , absoluteBlock % 4),end="")
if status == self.OK:
status, block = self.read(absoluteBlock)
if status == self.ERR:
break
else:
for value in block:
print("{:02X} ".format(value),end="")
print(" ",end="")
for value in block:
if (value > 0x20) and (value < 0x7f):
print(chr(value),end="")
else:
print('.',end="")
print("")
else:
break
if status == self.ERR:
print("Authentication error")
return self.ERR
return self.OK
Embedded C:
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/spi.h"
#include "MFRC522.h"
// Pin definitions
#define RED_LED 0
#define GREEN_LED 1
#define BLUE_LED 2
void init_leds() {
gpio_init(RED_LED);
gpio_set_dir(RED_LED, GPIO_OUT);
gpio_init(GREEN_LED);
gpio_set_dir(GREEN_LED, GPIO_OUT);
gpio_init(BLUE_LED);
gpio_set_dir(BLUE_LED, GPIO_OUT);
gpio_put(RED_LED, 0);
gpio_put(GREEN_LED, 0);
gpio_put(BLUE_LED, 0);
}
int main() {
stdio_init_all();
spi_init(spi0, 1000 * 1000);
MFRC522_Init();
init_leds();
printf("Bring TAG closer...\n");
while (true) {
if (MFRC522_Request(PICC_REQIDL, NULL) == MI_OK) {
uint8_t uid[5];
if (MFRC522_Anticoll(uid) == MI_OK) {
uint32_t card = (uid[0] << 24) | (uid[1] << 16) | (uid[2] << 8) | uid[3];
printf("CARD ID: %u\n", card);
gpio_put(RED_LED, 0);
gpio_put(GREEN_LED, 0);
gpio_put(BLUE_LED, 0);
if (card == 4120449875) {
gpio_put(GREEN_LED, 1);
} else if (card == 3310611155) {
gpio_put(BLUE_LED, 1);
} else {
gpio_put(RED_LED, 1);
}
}
}
sleep_ms(500);
}
return 0;
}
MFRC522.h:
#ifndef MFRC522_H
#define MFRC522_H
#include <stdint.h>
#define CommandReg 0x01
#define ComIEnReg 0x02
#define DivIEnReg 0x03
#define ComIrqReg 0x04
#define ErrorReg 0x06 // Error register
#define Status1Reg 0x07
#define Status2Reg 0x08
#define ModeReg 0x11
#define TxControlReg 0x14
#define RxControlReg 0x13
#define FIFODataReg 0x09
#define FIFOLevelReg 0x0A
#define CRCResultRegL 0x22
#define CRCResultRegH 0x21
#define VersionReg 0x37
// MFRC522 Commands
#define PCD_IDLE 0x00
#define PCD_AUTHENT 0x0E
#define PCD_RECEIVE 0x08
#define PCD_TRANSMIT 0x04
#define PCD_TRANSCEIVE 0x0C
#define PCD_RESET_PH 0x0F
#define PCD_CALCCRC 0x03
// MFRC522 PICC Commands
#define PICC_REQIDL 0x26
#define PICC_ANTICOLL 0x93
// Status codes
#define MI_OK 0
#define MI_NOTAGERR 1
#define MI_ERR 2
// Function declarations
void MFRC522_Init(void);
void MFRC522_Reset(void);
void MFRC522_AntennaOn(void);
uint8_t MFRC522_Request(uint8_t reqMode, uint8_t *TagType);
uint8_t MFRC522_Anticoll(uint8_t *serNum);
void MFRC522_WriteRegister(uint8_t reg, uint8_t value);
uint8_t MFRC522_ReadRegister(uint8_t reg);
uint8_t MFRC522_ToCard(uint8_t command, uint8_t *sendData, uint8_t sendLen, uint8_t *backData, uint32_t *backLen);
#endif
MFRC522.c:
#include "MFRC522.h"
#include "hardware/spi.h"
#include "hardware/gpio.h"
// Pin definitions for SPI and RFID
#define SPI_PORT spi0
#define RST_PIN 22
#define CS_PIN 5
// MFRC522 Registers
#define CommandReg 0x01
#define CommIEnReg 0x02
#define DivIEnReg 0x03
#define CommIrqReg 0x04
#define FIFOLevelReg 0x0A
#define FIFODataReg 0x09
#define ControlReg 0x0C
#define BitFramingReg 0x0D
#define ModeReg 0x11
#define TxControlReg 0x14
#define ErrorReg 0x06
void MFRC522_WriteRegister(uint8_t reg, uint8_t value) {
gpio_put(CS_PIN, 0); // Select RFID
spi_write_blocking(SPI_PORT, ®, 1);
spi_write_blocking(SPI_PORT, &value, 1);
gpio_put(CS_PIN, 1); // Deselect RFID
}
uint8_t MFRC522_ReadRegister(uint8_t reg) {
uint8_t value;
reg = (reg << 1) | 0x80;
gpio_put(CS_PIN, 0); // Select RFID
spi_write_blocking(SPI_PORT, ®, 1);
spi_read_blocking(SPI_PORT, 0, &value, 1);
gpio_put(CS_PIN, 1); // Deselect RFID
return value;
}
void MFRC522_Init(void) {
// Initialize SPI
spi_init(SPI_PORT, 1000000);
gpio_set_function(6, GPIO_FUNC_SPI); // SCK
gpio_set_function(7, GPIO_FUNC_SPI); // MOSI
gpio_set_function(4, GPIO_FUNC_SPI); // MISO
// Initialize RST and CS
gpio_init(RST_PIN);
gpio_set_dir(RST_PIN, GPIO_OUT);
gpio_put(RST_PIN, 1); // Set RST high
gpio_init(CS_PIN);
gpio_set_dir(CS_PIN, GPIO_OUT);
gpio_put(CS_PIN, 1); // Set CS high
MFRC522_Reset();
MFRC522_WriteRegister(TxControlReg, 0x03); // Enable antenna
}
void MFRC522_Reset(void) {
MFRC522_WriteRegister(CommandReg, PCD_RESET_PH);
}
uint8_t MFRC522_Request(uint8_t reqMode, uint8_t *TagType) {
uint8_t status;
uint32_t backBits;
MFRC522_WriteRegister(BitFramingReg, 0x07);
TagType[0] = reqMode;
status = MFRC522_ToCard(PCD_TRANSCEIVE, TagType, 1, TagType, &backBits);
return (status == MI_OK && backBits == 0x10) ? MI_OK : MI_ERR;
}
uint8_t MFRC522_Anticoll(uint8_t *serNum) {
uint8_t status;
uint8_t serNumCheck = 0;
uint32_t unLen;
MFRC522_WriteRegister(BitFramingReg, 0x00);
serNum[0] = PICC_ANTICOLL;
serNum[1] = 0x20;
status = MFRC522_ToCard(PCD_TRANSCEIVE, serNum, 2, serNum, &unLen);
if (status == MI_OK) {
for (int i = 0; i < 4; i++) {
serNumCheck ^= serNum[i];
}
if (serNumCheck != serNum[4]) {
status = MI_ERR;
}
}
return status;
}
uint8_t MFRC522_ToCard(uint8_t command, uint8_t *sendData, uint8_t sendLen, uint8_t *backData, uint32_t *backLen) {
uint8_t status = MI_ERR;
uint8_t irqEn = 0x77;
uint8_t waitIRq = 0x30;
uint8_t lastBits;
uint8_t n;
uint32_t i;
MFRC522_WriteRegister(CommIEnReg, irqEn | 0x80);
MFRC522_WriteRegister(CommandReg, PCD_IDLE);
MFRC522_WriteRegister(FIFOLevelReg, 0x80);
for (i = 0; i < sendLen; i++) {
MFRC522_WriteRegister(FIFODataReg, sendData[i]);
}
MFRC522_WriteRegister(CommandReg, command);
if (command == PCD_TRANSCEIVE) {
MFRC522_WriteRegister(BitFramingReg, 0x80);
}
i = 2000;
do {
n = MFRC522_ReadRegister(CommIrqReg);
i--;
} while (i != 0 && !(n & 0x01) && !(n & waitIRq));
if (i != 0) {
if (!(MFRC522_ReadRegister(ErrorReg) & 0x1B)) {
status = MI_OK;
if (n & irqEn & 0x01) {
status = MI_NOTAGERR;
}
if (command == PCD_TRANSCEIVE) {
n = MFRC522_ReadRegister(FIFOLevelReg);
lastBits = MFRC522_ReadRegister(ControlReg) & 0x07;
*backLen = (n - 1) * 8 + lastBits;
if (n == 0) {
n = 1;
}
if (*backLen > 0) {
for (i = 0; i < n; i++) {
backData[i] = MFRC522_ReadRegister(FIFODataReg);
}
}
}
} else {
status = MI_ERR;
}
}
return status;
}
void MFRC522_AntennaOn(void) {
uint8_t temp = MFRC522_ReadRegister(TxControlReg);
if (!(temp & 0x03)) {
MFRC522_WriteRegister(TxControlReg, temp | 0x03);
}
}
Hardware Image:
Output:
The expected output when running the program on the Raspberry Pi Pico will be the UID of the detected RFID tag displayed in the serial console.

Video Demonstration:
Appendix:
Common CMakeLists:
cmake_minimum_required(VERSION 3.12)
# Pull in SDK (must be before project)
include(pico_sdk_import.cmake)
project(pico_experiments C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
if (PICO_SDK_VERSION_STRING VERSION_LESS "1.3.0")
message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.3.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}")
endif()
# Initialize the SDK
pico_sdk_init()
add_compile_options(-Wall
-Wno-format # int != int32_t as far as the compiler is concerned because gcc has int32_t as long int
-Wno-unused-function # we have some for the docs that arent called
)
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
add_compile_options(-Wno-maybe-uninitialized)
endif()
# Hardware-specific examples in subdirectories:
add_subdirectory(expt_10_LCD)
CMakeLists:
# Set the minimum CMake version required
cmake_minimum_required(VERSION 3.13)
# Define the project name
project(MFRC522_Project)
# Initialize the Pico SDK (adjust the path if required)
include(pico_sdk_import.cmake)
pico_sdk_init()
# Define your executable and source files
add_executable(MFRC522_Project
main.c # Your main application code
MFRC522.c # MFRC522 library implementation
)
# Link any libraries that your project needs
target_link_libraries(MFRC522_Project
pico_stdlib # Standard Pico SDK libraries
hardware_spi # SPI library for hardware communication
)
# Enable USB or UART (depending on your serial communication preferences)
pico_enable_stdio_usb(MFRC522_Project 1) # Enable USB for debug output
pico_enable_stdio_uart(MFRC522_Project 0) # Disable UART for this project
# Create additional output formats (like UF2, bin, hex)
pico_add_extra_outputs(MFRC522_Project)
References:
| # | Topic | Date & time | Action |
|---|
0% Completed (0/1)