Source code for SEEL.SENSORS.MF522

# -*- coding: utf-8 -*-
# MF522 - Software stack to access the MF522 RFID reader via the SEELablet
#
# Copyright (C) 2015 by Jithin B.P. <jithinbp@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

from __future__ import print_function
from SEEL import interface
import time
[docs]def connect(I,cs): return MF522(I,cs)
[docs]class MF522: #Constants from https://github.com/miguelbalboa/rfid ( Open source License : UNLICENSE) CommandReg = 0x01 << 1 # starts and stops command execution ComIEnReg = 0x02 << 1 # enable and disable interrupt request control bits DivIEnReg = 0x03 << 1 # enable and disable interrupt request control bits ComIrqReg = 0x04 << 1 # interrupt request bits DivIrqReg = 0x05 << 1 # interrupt request bits ErrorReg = 0x06 << 1 # error bits showing the error status of the last command executed Status1Reg = 0x07 << 1 # communication status bits Status2Reg = 0x08 << 1 # receiver and transmitter status bits FIFODataReg = 0x09 << 1 # input and output of 64 byte FIFO buffer FIFOLevelReg = 0x0A << 1 # number of bytes stored in the FIFO buffer WaterLevelReg = 0x0B << 1 # level for FIFO underflow and overflow warning ControlReg = 0x0C << 1 # miscellaneous control registers BitFramingReg = 0x0D << 1 # adjustments for bit-oriented frames CollReg = 0x0E << 1 # bit position of the first bit-collision detected on the RF interface ModeReg = 0x11 << 1 # defines general modes for transmitting and receiving TxModeReg = 0x12 << 1 # defines transmission data rate and framing RxModeReg = 0x13 << 1 # defines reception data rate and framing TxControlReg = 0x14 << 1 # controls the logical behavior of the antenna driver pins TX1 and TX2 TxASKReg = 0x15 << 1 # controls the setting of the transmission modulation TxSelReg = 0x16 << 1 # selects the internal sources for the antenna driver RxSelReg = 0x17 << 1 # selects internal receiver settings RxThresholdReg = 0x18 << 1 # selects thresholds for the bit decoder DemodReg = 0x19 << 1 # defines demodulator settings MfTxReg = 0x1C << 1 # controls some MIFARE communication transmit parameters MfRxReg = 0x1D << 1 # controls some MIFARE communication receive parameters SerialSpeedReg = 0x1F << 1 # selects the speed of the serial UART interface CRCResultRegH = 0x21 << 1 # shows the MSB and LSB values of the CRC calculation CRCResultRegL = 0x22 << 1 ModWidthReg = 0x24 << 1 # controls the ModWidth setting? RFCfgReg = 0x26 << 1 # configures the receiver gain GsNReg = 0x27 << 1 # selects the conductance of the antenna driver pins TX1 and TX2 for modulation CWGsPReg = 0x28 << 1 # defines the conductance of the p-driver output during periods of no modulation ModGsPReg = 0x29 << 1 # defines the conductance of the p-driver output during periods of modulation TModeReg = 0x2A << 1 # defines settings for the internal timer TPrescalerReg = 0x2B << 1 # the lower 8 bits of the TPrescaler value. The 4 high bits are in TModeReg. TReloadRegH = 0x2C << 1 # defines the 16-bit timer reload value TReloadRegL = 0x2D << 1 TCounterValueRegH = 0x2E << 1 # shows the 16-bit timer value TCounterValueRegL = 0x2F << 1 TestSel1Reg = 0x31 << 1 # general test signal configuration TestSel2Reg = 0x32 << 1 # general test signal configuration TestPinEnReg = 0x33 << 1 # enables pin output driver on pins D1 to D7 TestPinValueReg = 0x34 << 1 # defines the values for D1 to D7 when it is used as an I/O bus TestBusReg = 0x35 << 1 # shows the status of the internal test bus AutoTestReg = 0x36 << 1 # controls the digital self test VersionReg = 0x37 << 1 # shows the software version AnalogTestReg = 0x38 << 1 # controls the pins AUX1 and AUX2 TestDAC1Reg = 0x39 << 1 # defines the test value for TestDAC1 TestDAC2Reg = 0x3A << 1 # defines the test value for TestDAC2 TestADCReg = 0x3B << 1 # shows the value of ADC I and Q channels # MFRC522 commands. Described in chapter 10 of the datasheet. PCD_Idle = 0x00 #no action, cancels current command execution PCD_Mem = 0x01 #stores 25 bytes into the internal buffer PCD_GenerateRandomID = 0x02 #generates a 10-byte random ID number PCD_CalcCRC = 0x03 #activates the CRC coprocessor or performs a self test PCD_Transmit = 0x04 # transmits data from the FIFO buffer PCD_NoCmdChange = 0x07 PCD_Receive = 0x08 #activates the receiver circuits PCD_Transceive = 0x0C #transmits data from FIFO buffer to antenna and automatically activates the receiver after transmission PCD_MFAuthent = 0x0E #performs the MIFARE standard authentication as a reader PCD_SoftReset = 0x0F #resets the MFRC522 RxGain_18dB = 0x00 << 4 # 000b - 18 dB, minimum RxGain_23dB = 0x01 << 4 # 001b - 23 dB RxGain_18dB_2 = 0x02 << 4 # 010b - 18 dB, it seems 010b is a duplicate for 000b RxGain_23dB_2 = 0x03 << 4 # 011b - 23 dB, it seems 011b is a duplicate for 001b RxGain_33dB = 0x04 << 4 # 100b - 33 dB, average, and typical default RxGain_38dB = 0x05 << 4 # 101b - 38 dB RxGain_43dB = 0x06 << 4 # 110b - 43 dB RxGain_48dB = 0x07 << 4 # 111b - 48 dB, maximum RxGain_min = 0x00 << 4 # 000b - 18 dB, minimum, convenience for RxGain_18dB RxGain_avg = 0x04 << 4 # 100b - 33 dB, average, convenience for RxGain_33dB RxGain_max = 0x07 << 4 # 111b - 48 dB, maximum, convenience for RxGain_48dB # The commands used by the PCD to manage communication with several PICCs (ISO 14443-3, Type A, section 6.4) PICC_CMD_REQA = 0x26 # REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or selection PICC_CMD_WUPA = 0x52 # Wake-UP command, prepare for anticollision or selection. 7 bit frame. PICC_CMD_CT = 0x88 # Cascade Tag. Not really a command, but used during anti collision. PICC_CMD_SEL_CL1 = 0x93 # Anti collision/Select, Cascade Level 1 PICC_CMD_SEL_CL2 = 0x95 # Anti collision/Select, Cascade Level 2 PICC_CMD_SEL_CL3 = 0x97 # Anti collision/Select, Cascade Level 3 PICC_CMD_HLTA = 0x50 # HaLT command, Type A. Instructs an ACTIVE PICC to go to state HALT. # The commands used for MIFARE Classic (from http://www.mouser.com/ds/2/302/MF1S503x-89574.pdf, Section 9) # Use PCD_MFAuthent to authenticate access to a sector, then use these commands to read/write/modify the blocks on the sector. # The read/write commands can also be used for MIFARE Ultralight. PICC_CMD_MF_AUTH_KEY_A = 0x60 # Perform authentication with Key A PICC_CMD_MF_AUTH_KEY_B = 0x61 # Perform authentication with Key B PICC_CMD_MF_READ = 0x30 # Reads one 16 byte block from the authenticated sector of the PICC. Also used for MIFARE Ultralight. PICC_CMD_MF_WRITE = 0xA0 # Writes one 16 byte block to the authenticated sector of the PICC. Called "COMPATIBILITY WRITE" for MIFARE Ultralight. PICC_CMD_MF_DECREMENT = 0xC0 # Decrements the contents of a block and stores the result in the internal data register. PICC_CMD_MF_INCREMENT = 0xC1 # Increments the contents of a block and stores the result in the internal data register. PICC_CMD_MF_RESTORE = 0xC2 # Reads the contents of a block into the internal data register. PICC_CMD_MF_TRANSFER = 0xB0 # Writes the contents of the internal data register to a block. NRSTPD = 22 MAX_LEN = 16 MI_OK = 0 MI_NOTAGERR = 1 MI_ERR = 2 PCD_CALCCRC = 0x03 PICC_REQIDL = 0x26 PICC_REQALL = 0x52 PICC_ANTICOLL = 0x93 PICC_SElECTTAG = 0x93 PICC_AUTHENT1A = 0x60 PICC_AUTHENT1B = 0x61 PICC_READ = 0x30 PICC_WRITE = 0xA0 PICC_DECREMENT = 0xC0 PICC_INCREMENT = 0xC1 PICC_RESTORE = 0xC2 PICC_TRANSFER = 0xB0 PICC_HALT = 0x50 # The commands used for MIFARE Ultralight (from http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf, Section 8.6) # The PICC_CMD_MF_READ and PICC_CMD_MF_WRITE can also be used for MIFARE Ultralight. PICC_CMD_UL_WRITE = 0xA2 #Writes one 4 byte page to the PICC. MF_ACK = 0xA # The MIFARE Classic uses a 4 bit ACK/NAK. Any other value than 0xA is NAK. MF_KEY_SIZE = 6 # A Mifare Crypto1 key is 6 bytes. def __init__(self,I,cs='CS1'): self.cs=cs self.I = I self.I.SPI.set_parameters(2,1,1,0) if not self.reset(): self.connected=False return None self.write(self.TModeReg, 0x80) self.write(self.TPrescalerReg, 0xA9) self.write(self.TReloadRegH, 0x03) self.write(self.TReloadRegL, 0xE8) self.write(self.TxASKReg, 0x40) self.write(self.ModeReg, 0x3D) #Enable the antenna self.enableAntenna() self.connected = True
[docs] def MFRC522_Init(self): GPIO.output(self.NRSTPD, 1) self.MFRC522_Reset(); self.write(self.TModeReg, 0x8D) self.write(self.TPrescalerReg, 0x3E) self.write(self.TReloadRegL, 30) self.write(self.TReloadRegH, 0) self.write(self.TxAutoReg, 0x40) self.write(self.ModeReg, 0x3D) self.AntennaOn()
[docs] def enableAntenna(self): val = self.read(self.TxControlReg); if ((val & 0x03) != 0x03): self.write(self.TxControlReg, val | 0x03);
[docs] def reset(self): self.write(self.CommandReg,self.PCD_SoftReset) s=time.time() while (self.read(self.CommandReg) & (1<<4)): print ('wait') time.sleep(0.1) if time.time() - s > .5: return False return True
[docs] def write(self,register,val): self.I.SPI.set_cs(self.cs,0) ret = self.I.SPI.send16(((register&0x7F)<<8)|val) self.I.SPI.set_cs(self.cs,1) return ret&0xFF
[docs] def read(self,register): self.I.SPI.set_cs(self.cs,0) ret = self.I.SPI.send16((register|0x80)<<8) self.I.SPI.set_cs(self.cs,1) return ret&0xFF
[docs] def readMany(self,register,total): self.I.SPI.set_cs(self.cs,0) self.I.SPI.send8(register) vals = [] for a in range(total-1): vals.append( I.SPI.send8(register) ) vals.append( I.SPI.send8(0) ) self.I.SPI.set_cs(self.cs,1) return vals
[docs] def getStatus(self): return self.read(self.Status1Reg)
[docs] def getVersion(self): ver = self.read(self.VersionReg) if ver==0x88: print ('Cloned version: Fudan Semiconductors') elif ver==0x90: print ('version 1.0') elif ver==0x91: print ('version 1.0') elif ver==0x92: print ('version 2.0') else: print ('Unknown version ',ver) return ver
[docs] def SetBitMask(self, reg, mask): tmp = self.read(reg) self.write(reg, tmp | mask)
[docs] def ClearBitMask(self, reg, mask): tmp = self.read(reg); self.write(reg, tmp & (~mask))
[docs] def MFRC522_ToCard(self,command,sendData): returnedData = [] backLen = 0 status = self.MI_ERR irqEn = 0x00 waitIRq = 0x00 lastBits = None n = 0 i = 0 if command == self.PCD_MFAuthent: irqEn = 0x12 waitIRq = 0x10 if command == self.PCD_Transceive: irqEn = 0x77 waitIRq = 0x30 self.write(self.ComIEnReg, irqEn|0x80) self.ClearBitMask(self.ComIrqReg, 0x80) self.SetBitMask(self.FIFOLevelReg, 0x80) self.write(self.CommandReg, self.PCD_Idle); for a in sendData: self.write(self.FIFODataReg, a) self.write(self.CommandReg, command) if command == self.PCD_Transceive: self.SetBitMask(self.BitFramingReg, 0x80) i = 2000 while True: n = self.read(self.ComIrqReg) i = i - 1 if ~((i!=0) and ~(n&0x01) and ~(n&waitIRq)): break self.ClearBitMask(self.BitFramingReg, 0x80) if i != 0: if (self.read(self.ErrorReg) & 0x1B)==0x00: status = self.MI_OK if n & irqEn & 0x01: status = self.MI_NOTAGERR if command == self.PCD_Transceive: n = self.read(self.FIFOLevelReg) lastBits = self.read(self.ControlReg) & 0x07 if lastBits != 0: backLen = (n-1)*8 + lastBits else: backLen = n*8 if n == 0: n = 1 if n > self.MAX_LEN: n = self.MAX_LEN i = 0 while i<n: returnedData.append(self.read(self.FIFODataReg)) i = i + 1; else: status = self.MI_ERR return (status,returnedData,backLen)
[docs] def MFRC522_Request(self, reqMode): status = None backBits = None TagType = [] self.write(self.BitFramingReg, 0x07) TagType.append(reqMode); (status,returnedData,backBits) = self.MFRC522_ToCard(self.PCD_Transceive, TagType) if ((status != self.MI_OK) | (backBits != 0x10)): status = self.MI_ERR return (status,backBits)
[docs] def MFRC522_Anticoll(self): returnedData = [] serNumCheck = 0 serNum = [] self.write(self.BitFramingReg, 0x00) serNum.append(self.PICC_ANTICOLL) serNum.append(0x20) (status,returnedData,backBits) = self.MFRC522_ToCard(self.PCD_Transceive,serNum) if(status == self.MI_OK): i = 0 if len(returnedData)==5: while i<4: serNumCheck = serNumCheck ^ returnedData[i] i = i + 1 if serNumCheck != returnedData[i]: status = self.MI_ERR else: status = self.MI_ERR return (status,returnedData)
[docs] def CalulateCRC(self, pIndata): self.ClearBitMask(self.DivIrqReg, 0x04) self.SetBitMask(self.FIFOLevelReg, 0x80); for a in pIndata: self.write(self.FIFODataReg, a) self.write(self.CommandReg, self.PCD_CALCCRC) for i in range(0xFF): n = self.read(self.DivIrqReg) if (n&0x04): break pOutData = [] pOutData.append(self.read(self.CRCResultRegL)) pOutData.append(self.read(self.CRCResultRegH)) return pOutData
[docs] def MFRC522_SelectTag(self, serNum): returnedData = [] buf = [] buf.append(self.PICC_SElECTTAG) buf.append(0x70) i = 0 while i<5: buf.append(serNum[i]) i = i + 1 pOut = self.CalulateCRC(buf) buf.append(pOut[0]) buf.append(pOut[1]) (status, returnedData, backLen) = self.MFRC522_ToCard(self.PCD_Transceive, buf) if (status == self.MI_OK) and (backLen == 0x18): return returnedData[0] else: return 0
[docs] def MFRC522_Auth(self, authMode, BlockAddr, Sectorkey, serNum): buff = [] # First byte should be the authMode (A or B) buff.append(authMode) # Second byte is the trailerBlock (usually 7) buff.append(BlockAddr) # Now we need to append the authKey which usually is 6 bytes of 0xFF i = 0 while(i < len(Sectorkey)): buff.append(Sectorkey[i]) i = i + 1 i = 0 # Next we append the first 4 bytes of the UID while(i < 4): buff.append(serNum[i]) i = i +1 # Now we start the authentication itself (status, returnedData, backLen) = self.MFRC522_ToCard(self.PCD_MFAuthent,buff) # Check if an error occurred if not(status == self.MI_OK): print ("AUTH ERROR!!") if not (self.read(self.Status2Reg) & 0x08) != 0: print ("AUTH ERROR(status2reg & 0x08) != 0") # Return the status return status
[docs] def MFRC522_StopCrypto1(self): self.ClearBitMask(self.Status2Reg, 0x08) self.SetBitMask(self.CommandReg, 0x10)
[docs] def MFRC522_Read(self, blockAddr): recvData = [] recvData.append(self.PICC_READ) recvData.append(blockAddr) pOut = self.CalulateCRC(recvData) recvData.append(pOut[0]) recvData.append(pOut[1]) (status, returnedData, backLen) = self.MFRC522_ToCard(self.PCD_Transceive, recvData) if not(status == self.MI_OK): print ("Error while reading!") i = 0 return returnedData
[docs] def MFRC522_Write(self, blockAddr, writeData): buff = [] buff.append(self.PICC_WRITE) buff.append(blockAddr) crc = self.CalulateCRC(buff) buff.append(crc[0]) buff.append(crc[1]) (status, returnedData, backLen) = self.MFRC522_ToCard(self.PCD_Transceive, buff) if not(status == self.MI_OK) or not(backLen == 4) or not((returnedData[0] & 0x0F) == 0x0A): status = self.MI_ERR print (str(backLen)+" returnedData &0x0F == 0x0A "+str(returnedData[0]&0x0F)) if status == self.MI_OK: i = 0 buf = [] while i < 16: buf.append(writeData[i]) i = i + 1 crc = self.CalulateCRC(buf) buf.append(crc[0]) buf.append(crc[1]) (status, returnedData, backLen) = self.MFRC522_ToCard(self.PCD_Transceive,buf) if not(status == self.MI_OK) or not(backLen == 4) or not((returnedData[0] & 0x0F) == 0x0A): print ("Error while writing") if status == self.MI_OK: print ("Data written")
[docs] def MFRC522_DumpClassic1K(self, key, uid): i = 0 while i < 64: status = self.MFRC522_Auth(self.PICC_AUTHENT1A, i, key, uid) # Check if authenticated if status == self.MI_OK: self.MFRC522_Read(i) else: print ("Authentication error") i = i+1
if __name__ == "__main__": from SEEL import interface I= interface.connect() A = MF522(I,'CS1') ret = A.getStatus() print (ret,hex(ret),bin(ret)) A.getVersion() import time while 1: (status,TagType) = A.MFRC522_Request(A.PICC_CMD_REQA) if status == A.MI_OK: print ("Card detected") (status,uid) = A.MFRC522_Anticoll() if status == A.MI_OK: print ("Card read UID: "+str(uid[0])+","+str(uid[1])+","+str(uid[2])+","+str(uid[3])) key = [0xFF,0xFF,0xFF,0xFF,0xFF,0xFF] A.MFRC522_SelectTag(uid) status = A.MFRC522_Auth(A.PICC_AUTHENT1A, 8, key, uid) if status == A.MI_OK: A.MFRC522_Read(8) # Variable for the data to write ''' data = [] # Fill the data with 0xFF for x in range(0,16): data.append(0xFF) print ("Sector 8 looked like this:") # Read block 8 A.MFRC522_Read(8) print ("\n") print ("Sector 8 will now be filled with 0xFF:") # Write the data A.MFRC522_Write(8, data) print ("\n") print ("It now looks like this:") # Check to see if it was written A.MFRC522_Read(8) print ("\n") ''' A.MFRC522_StopCrypto1() else: print ("Authentication error") else: print ('not detected') #A.reset()