/pi-rc522

Raspberry Pi Python library for SPI RFID RC522 module

Primary LanguagePython

Python RC522 library

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

Based on MFRC522-python.

You'll need SPI-Py.

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 all from first sector - sector 0. Block addresses 4, 5, 6, 7 are from 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 the last block of this sector - 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
SDA 1 24 GPIO8, CE0
SCK 2 23 GPIO11, SCKL
MOSI 3 19 GPIO10, MOSI
MISO 4 21 GPIO9, MISO
GND 6 6, 9, 20, 25 Ground
RST 7 22 GPIO25
3.3V 8 1 3V3

You can connect SDA pin also to CE1 (GPIO7, pin #26) and call the RFID constructor with dev='/dev/spidev0.0' and you can connect RST pin to any other free GPIO pin and call the constructor with pin_rst=BOARD numbering pin.

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 alghoritms 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 to Read and KeyChange modules for RFIDUtil usage example.

rdr = RFID.RFID()
while True:
  (error, tag_type) = rdr.request()
  if not error:
    print "Tag detected"
    (error, uid) = rdr.anticoll()
    if not error:
      print "UID: " + str(uid)
      #Auth for block 10 (block 2 of sector 2) using default shipping key A
      if 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()
      
      
#Calls GPIO cleanup
rdr.cleanup()

Util usage

RFIDUtil contains few useful methods for dealing with tags.

import RFID
import signal
import time

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

while True:
    #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()
        
            time.sleep(1)