wendlers / micropython-mfrc522

(Micro)Python class to access the MFRC522 RFID reader
MIT License
162 stars 114 forks source link

Improved documentation #1

Closed cefn closed 7 years ago

cefn commented 7 years ago

Hi there, @wendlers and thanks for giving me the confidence to combine mfrc522 with micropython in a project!

I have tested the read and write examples and they are functional on the Mifare 1k tag I have.

However, I am struggling to know, for example, how to write to any other parts of the card than '8', whatever that means. If I could figure it out, I could perhaps prepare a PR with documentation which someone could sanity-check for me.

Given in both read.py and write.py only 16 bytes are manipulated, it must be a single sector and block which is being read/written, but I cannot pin down where the block index is specified, assuming 8 is the sector. Given a specific target sector has been unlocked (via auth) I understand I should be able to write up to the first 3 blocks of 16 bytes, but mystified how to fully specify sector and block for reads and writes I am afraid.

It's not clear from the reader's method signatures, whether the write method's addr relates to sector or block or some combination of the two (e.g. all blocks are numbered 0-64). It can't be block as there's only 4 blocks, and the demo writes to 8. However, if that isn't the block address, I am struggling to find at which point of the api you can, for example address block 2 in sector 8.

Equally, the auth method which unlocks access to a sector takes a 'sect' argument, but in the write.py example a 'key' is passed to it, containing multiple bytes, so that can't be the sector argument. There appears to just be a single addr argument throughout, rather than separate indexes for sector and block.

All in all, I am struggling to interpret the available functions without documentation, at least identifying what the parameters represent, given you have sensibly given them short names for a low-resource environment like micropython.

cefn commented 7 years ago

OK I've made progress on this question and I have a reference implementation which reads all sectors and writes to every legitimate sector of a Mifare Classic 1k card. This is based on

The issue I was having was that by default the routine I was using to check sector write then read was trying to write to 0 which it shouldn't do, and this was killing the procedure, not that there was anything wrong with the keys or the addressing model.

I suspect I am doing more auth than I need to in this implementation since auth appears to be handled on a block level, when I understand from other documentation that unlocking happens on a sector level.

import mfrc522
from os import uname

if uname()[0] == 'WiPy':
    rdr = mfrc522.MFRC522("GP14", "GP16", "GP15", "GP22", "GP17")
elif uname()[0] == 'esp8266':
    rdr = mfrc522.MFRC522(0, 2, 4, 5, 14)
else:
    raise RuntimeError("Unsupported")

def read_once():

    print("Place card")

    (stat, tag_type) = rdr.request(rdr.REQIDL)

    if stat == rdr.OK:

        (stat, raw_uid) = rdr.anticoll()

        if stat == rdr.OK:
            print("Detected")
            print("type: 0x%02x" % tag_type)
            print("uid: 0x%02x%02x%02x%02x" % (raw_uid[0], raw_uid[1], raw_uid[2], raw_uid[3]))
            print("")

            if rdr.select_tag(raw_uid) == rdr.OK:

                key = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]

                for sector in range(1,64):
                    if rdr.auth(rdr.AUTHENT1A, sector, key, raw_uid) == rdr.OK:
                        print("data@%d: %s" % (sector, rdr.read(sector)))
                    else:
                        print("Auth err")
                rdr.stop_crypto1()
            else:
                print("Select failed")

def write_once():
    print("Place card")
    (stat, tag_type) = rdr.request(rdr.REQIDL) # check if antenna idle
    if stat == rdr.OK: # at least one card detected
        (stat, raw_uid) = rdr.anticoll()
        if stat == rdr.OK: # able to separate a single card
            print("Detected")
            print("type: 0x%02x" % tag_type)
            print("uid: 0x%02x%02x%02x%02x" % (raw_uid[0], raw_uid[1], raw_uid[2], raw_uid[3]))
            print("")
            if rdr.select_tag(raw_uid) == rdr.OK: # identify which card you want to manipulate
                key = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]
                for blockIdx in range(64):
                    if blockIdx > 0 and blockIdx % 4 is not 3:
                        print("Writing" + str(blockIdx) + "...", end="")
                        if rdr.auth(rdr.AUTHENT1A, blockIdx, key, raw_uid) == rdr.OK:
                            stat = rdr.write(blockIdx, bytes([blockIdx for i in range(16)]))
                            if stat == rdr.OK:
                                print("Written")
                            else:
                                print("Failed")
                        else:
                                print("Auth err")
                rdr.stop_crypto1() # assuming this complements select_tag
            else:
                print("Select failed")

def await_removal():
    while True:
        (stat, tag_type) = rdr.request(rdr.REQIDL)  # check if antenna idle
        if stat != rdr.OK:
            return

def cycle():
    while True:
        read_once()
        await_removal()
        write_once()
        await_removal()

On a bodge/rush job to implement something at present, but hopefully will get to a PR with improved documentation soon.