Skip to content

Raspberry Pi Python library for SPI RFID RC522 module, updated fork for gpiozero and modern kernel compatibility

License

Notifications You must be signed in to change notification settings

hoffie/pi-rc522-gpiozero

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Python RC522 library

pi-rc522 consists of two Python classes for controlling an SPI RFID module "RC522" using Raspberry Pi or Beaglebone Black. You can get this module on AliExpress or Ebay for $3.

This fork makes the library compatible with recent kernels (current Raspberry Pi OS based on Debian bookworm). The original library uses RPi.GPIO, which has not been updated to work with /sys/class/gpio-less kernels yet. The fork uses gpiozero, which works properly. This fork therefore fixes the RuntimeError: Failed to add edge detection crash when using the IRQ pin.

Based on lmurach's pi-rc522, which is based on MFRC522-python.

Install using pip:

pip install pi-rc522

Or get source code from Github:

git clone https://github.com/ondryaso/pi-rc522.git
cd pi-rc522
python setup.py install

You'll also need to install the gpiozero, spidev and RPi.GPIO libraries on Raspberry PI. Beaglebone Black is untested in this fork (might work via gpiozero?).

MIFARE datasheet can be useful.

Sectors? Blocks?

Classic 1K MIFARE tag has 16 sectors, each contains 4 blocks. Each block has 16 bytes. All this stuff is indexed - you must count from zero. The library uses "block addresses", which are positions of blocks - so block address 5 is second block of second sector, thus it's block 1 of sector 1 (indexes). Block addresses 0, 1, 2, 3 are from the first sector - sector 0. Block addresses 4, 5, 6, 7 are from the second sector - sector 1, and so on. You should not write to first block - S0B0, because it contains manufacturer data. Each sector has it's sector trailer, which is located at it's last block - block 3. This block contains keys and access bits for corresponding sector. For more info, look at page 10 of the datasheet. You can use this useful utility to calculate access bits.

Connecting

Connecting RC522 module to SPI is pretty easy. You can use this neat website for reference.

Board pin name Board pin Physical RPi pin RPi pin name Beaglebone Black pin name
SDA 1 24 GPIO8, CE0 P9_17, SPI0_CS0
SCK 2 23 GPIO11, SCKL P9_22, SPI0_SCLK
MOSI 3 19 GPIO10, MOSI P9_18, SPI0_D1
MISO 4 21 GPIO9, MISO P9_21, SPI0_D0
IRQ 5 18 GPIO24 P9_15, GPIO_48
GND 6 6, 9, 20, 25 Ground Ground
RST 7 22 GPIO25 P9_23, GPIO_49
3.3V 8 1,17 3V3 VDD_3V3

You can also connect the SDA pin to CE1 (GPIO7, pin #26) and call the RFID constructor with bus=0, device=1 and you can connect RST pin to any other free GPIO pin and call the constructor with pin_rst=BOARD numbering pin. Furthermore, the IRQ pin is configurable by passing pin_irq=BOARD numbering pin.

NOTE: For RPi A+/B+/2/3 with 40 pin connector, SPI1/2 is available on top of SPI0. Kernel 4.4.x or higher and dtoverlay configuration is required. For SPI1/2, pin_ce=BOARD numbering pin is required.

NOTE: On Beaglebone Black, use pin names (e.g. "P9_17").

NOTE: On Beaglebone Black, generally you have to enable the SPI for the spidev device to show up; you can enable SPI0 by doing echo BB-SPIDEV0 > /sys/devices/bone_capemgr.9/slots. SPI1 is available only if you disable HDMI.

NOTE: If you are not using IRQ, you can pass pin_irq = None to the constructor.

You may change BOARD pinout to BCM py passing pin_mode=RPi.GPIO.BCM. Please note, that you then have to define all pins (irq+rst, ce if neccessary). Otherwise they would default to perhaps wrong pins (rst to pin 15/GPIO22, irq to pin 12/GPIO18).

Usage

The library is split to two classes - RFID and RFIDUtil. You can use only RFID, RFIDUtil just makes life a little bit better. You basically want to start with while True loop and "poll" the tag state. That's done using request method. Most of the methods return error state, which is simple boolean - True is error, False is not error. The request method returns True if tag is not present. If request is successful, you should call anticoll method. It runs anti-collision algorithms and returns used tag UID, which you'll use for select_tag method. Now you can do whatever you want. Important methods are documented. You can also look at the Read and KeyChange examples for RFIDUtil usage.

from pirc522 import RFID
rdr = RFID()

try:
  while True:
    rdr.wait_for_tag()
    (error, tag_type) = rdr.request()
    if not error:
      print("Tag detected")
      (error, uid) = rdr.anticoll()
      if not error:
        print("UID: " + str(uid))
        # Select Tag is required before Auth
        if not rdr.select_tag(uid):
          # Auth for block 10 (block 2 of sector 2) using default shipping key A
          if not rdr.card_auth(rdr.auth_a, 10, [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], uid):
            # This will print something like (False, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
            print("Reading block 10: " + str(rdr.read(10)))
            # Always stop crypto1 when done working
            rdr.stop_crypto()

except KeyboardInterrupt:
  # Graceful shutdown
  rdr.cleanup()

To retrieve tag UID in either 4 or 7 byte form, you can use:

from pirc522 import RFID
rdr = RFID()

while True:
  rdr.wait_for_tag()
  uid = rdr.read_id(as_number = True)
  if uid is not None:
    print(f'UID: {uid:X}')

# Graceful shutdown
reader.cleanup()

Util usage

RFIDUtil contains a few useful methods for dealing with tags.

from pirc522 import RFID
import signal
import time

rdr = RFID()
util = rdr.util()
# Set util debug to true - it will print what's going on
util.debug = True

while True:
    # Wait for tag
    rdr.wait_for_tag()

    # Request tag
    (error, data) = rdr.request()
    if not error:
        print("\nDetected")

        (error, uid) = rdr.anticoll()
        if not error:
            # Print UID
            print("Card read UID: "+str(uid[0])+","+str(uid[1])+","+str(uid[2])+","+str(uid[3]))

            # Set tag as used in util. This will call RFID.select_tag(uid)
            util.set_tag(uid)
            # Save authorization info (key B) to util. It doesn't call RFID.card_auth(), that's called when needed
            util.auth(rdr.auth_b, [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])
            # Print contents of block 4 in format "S1B0: [contents in decimal]". RFID.card_auth() will be called now
            util.read_out(4)
            # Print it again - now auth won't be called, because it doesn't have to be
            util.read_out(4)
            # Print contents of different block - S1B2 - RFID.card_auth() will be called again
            util.read_out(6)
            # We can change authorization info if you have different key in other sector
            util.auth(rdr.auth_a, [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])
            #If you want to use methods from RFID itself, you can use this for authorization
            # This will authorize for block 1 of sector 2 -> block 9
            # This is once more called only if it's not already authorized for this block
            util.do_auth(util.block_addr(2, 1))
            # Now we can do some "lower-level" stuff with block 9
            rdr.write(9, [0x01, 0x23, 0x45, 0x67, 0x89, 0x98, 0x76, 0x54, 0x32, 0x10, 0x69, 0x27, 0x46, 0x66, 0x66, 0x64])
            # We can rewrite specific bytes in block using this method. None means "don't change this byte"
            # Note that this won't do authorization, because we've already called do_auth for block 9
            util.rewrite(9, [None, None, 0xAB, 0xCD, 0xEF])
            # This will write S2B1: [0x01, 0x23, 0xAB, 0xCD, 0xEF, 0x98, 0x76......] because we've rewritten third, fourth and fifth byte
            util.read_out(9)
            # Let's see what do we have in whole tag
            util.dump()
            # We must stop crypto
            util.deauth()

About

Raspberry Pi Python library for SPI RFID RC522 module, updated fork for gpiozero and modern kernel compatibility

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Python 100.0%