diff --git a/mfrc522/MFRC522.py b/mfrc522/MFRC522.py index 1698351..f2fee7c 100644 --- a/mfrc522/MFRC522.py +++ b/mfrc522/MFRC522.py @@ -26,6 +26,7 @@ import time import logging + class MFRC522: MAX_LEN = 16 @@ -125,31 +126,35 @@ class MFRC522: serNum = [] - def __init__(self, bus=0, device=0, spd=1000000, pin_mode=10, pin_rst=-1, debugLevel='WARNING'): + def __init__(self, bus=0, device=0, spd=1000000, pin_mode=GPIO.BOARD, pin_rst=-1, log_verbose=False): + self.logger = logging.getLogger(self.__class__.__name__) + # Don't change the logger's level based on log_verbose + # This ensures that ERROR messages are always logged at ERROR level + # The log_verbose flag will be used by individual log statements + self.log_verbose = log_verbose + + self.logger.debug('Setting up SPI') self.spi = spidev.SpiDev() self.spi.open(bus, device) self.spi.max_speed_hz = spd - self.logger = logging.getLogger('mfrc522Logger') - self.logger.addHandler(logging.StreamHandler()) - level = logging.getLevelName(debugLevel) - self.logger.setLevel(level) - gpioMode = GPIO.getmode() - if gpioMode is None: GPIO.setmode(pin_mode) else: pin_mode = gpioMode - + if pin_rst == -1: if pin_mode == 11: pin_rst = 15 else: pin_rst = 22 - + + self.logger.debug('Setting up RST') GPIO.setup(pin_rst, GPIO.OUT) GPIO.output(pin_rst, 1) + + self.logger.debug('Setting up MFRC522') self.MFRC522_Init() def MFRC522_Reset(self): @@ -191,6 +196,13 @@ def MFRC522_ToCard(self, command, sendData): lastBits = None n = 0 + # Log command as hex value + if self.log_verbose: + self.logger.debug({ + 'action': 'MFRC522_ToCard_start', + 'command': f'0x{command:02X}' # Log command as hex value + }) + if command == self.PCD_AUTHENT: irqEn = 0x12 waitIRq = 0x10 @@ -246,35 +258,64 @@ def MFRC522_ToCard(self, command, sendData): else: status = self.MI_ERR + # Log status and command as hex values + if self.log_verbose: + self.logger.debug({ + 'action': 'MFRC522_ToCard_complete', + 'command': f'0x{command:02X}', # Log command as hex value + 'status': f'0x{status:02X}', # Log status as hex value + 'backLen': backLen + }) + return (status, backData, backLen) def MFRC522_Request(self, reqMode): - status = None - backBits = None TagType = [] + # Log reqMode as hex value + if self.log_verbose: + self.logger.debug({ + 'action': 'MFRC522_Request_start', + 'reqMode': f'0x{reqMode:02X}' # Log reqMode as hex value + }) + self.Write_MFRC522(self.BitFramingReg, 0x07) TagType.append(reqMode) - (status, backData, backBits) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, TagType) + (status, backData, backBits) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, + TagType) if ((status != self.MI_OK) | (backBits != 0x10)): status = self.MI_ERR + # Log status and reqMode as hex values + if self.log_verbose: + self.logger.debug({ + 'action': 'MFRC522_Request_complete', + 'reqMode': f'0x{reqMode:02X}', # Log reqMode as hex value + 'status': f'0x{status:02X}' # Log status as hex value + }) + return (status, backBits) def MFRC522_Anticoll(self): - backData = [] serNumCheck = 0 - serNum = [] + # Log start of anticoll + if self.log_verbose: + self.logger.debug({ + 'action': 'MFRC522_Anticoll_start', + 'command': f'0x{self.PCD_TRANSCEIVE:02X}' # Log command as hex value + }) + self.Write_MFRC522(self.BitFramingReg, 0x00) serNum.append(self.PICC_ANTICOLL) serNum.append(0x20) - (status, backData, backBits) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, serNum) + (status, backData, backBits) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, + serNum) if (status == self.MI_OK): i = 0 @@ -286,6 +327,13 @@ def MFRC522_Anticoll(self): else: status = self.MI_ERR + # Log status as hex value + if self.log_verbose: + self.logger.debug({ + 'action': 'MFRC522_Anticoll_complete', + 'status': f'0x{status:02X}' # Log status as hex value + }) + return (status, backData) def CalulateCRC(self, pIndata): @@ -308,28 +356,55 @@ def CalulateCRC(self, pIndata): return pOutData def MFRC522_SelectTag(self, serNum): - backData = [] buf = [] buf.append(self.PICC_SElECTTAG) buf.append(0x70) - + + # Log start of select tag + if self.log_verbose: + self.logger.debug({ + 'action': 'MFRC522_SelectTag_start', + 'command': f'0x{self.PCD_TRANSCEIVE:02X}', # Log command as hex value + 'select_tag': f'0x{self.PICC_SElECTTAG:02X}' # Log select tag as hex value + }) + for i in range(5): buf.append(serNum[i]) pOut = self.CalulateCRC(buf) buf.append(pOut[0]) buf.append(pOut[1]) - (status, backData, backLen) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, buf) + (status, backData, backLen) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, + buf) + # Log status as hex value if (status == self.MI_OK) and (backLen == 0x18): - self.logger.debug("Size: " + str(backData[0])) - return backData[0] + if self.log_verbose: + self.logger.debug({ + 'action': 'MFRC522_SelectTag_complete', + 'status': f'0x{status:02X}', # Log status as hex value + 'size': backData[0] + }) + return status, backData[0] else: - return 0 + self.logger.debug({ + 'action': 'MFRC522_SelectTag_failed', + 'status': f'0x{status:02X}' # Log status as hex value + }) + return status, None def MFRC522_Auth(self, authMode, BlockAddr, Sectorkey, serNum): buff = [] + # Log start of auth with authMode as hex value + if self.log_verbose: + self.logger.debug({ + 'action': 'MFRC522_Auth_start', + 'authMode': f'0x{authMode:02X}', # Log authMode as hex value + 'blockAddr': BlockAddr, + 'command': f'0x{self.PCD_AUTHENT:02X}' # Log command as hex value + }) + # First byte should be the authMode (A or B) buff.append(authMode) @@ -345,13 +420,30 @@ def MFRC522_Auth(self, authMode, BlockAddr, Sectorkey, serNum): buff.append(serNum[i]) # Now we start the authentication itself - (status, backData, backLen) = self.MFRC522_ToCard(self.PCD_AUTHENT, buff) + (status, backData, backLen) = self.MFRC522_ToCard(self.PCD_AUTHENT, + buff) # Check if an error occurred if not (status == self.MI_OK): - self.logger.error("AUTH ERROR!!") + self.logger.error({ + 'error': "AUTH ERROR!!", + 'status': f'0x{status:02X}' # Log status as hex value + }) + if not (self.Read_MFRC522(self.Status2Reg) & 0x08) != 0: - self.logger.error("AUTH ERROR(status2reg & 0x08) != 0") + status2reg = self.Read_MFRC522(self.Status2Reg) + self.logger.error({ + 'error': "AUTH ERROR(status2reg & 0x08) != 0", + 'status': f'0x{status:02X}', # Log status as hex value + 'status2reg': f'0x{status2reg:02X}' # Log status2reg as hex value + }) + + # Log completion with status as hex value + if self.log_verbose: + self.logger.debug({ + 'action': 'MFRC522_Auth_complete', + 'status': f'0x{status:02X}' # Log status as hex value + }) # Return the status return status @@ -366,12 +458,40 @@ def MFRC522_Read(self, blockAddr): pOut = self.CalulateCRC(recvData) recvData.append(pOut[0]) recvData.append(pOut[1]) - (status, backData, backLen) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, recvData) + + # Log start of read operation + if self.log_verbose: + self.logger.debug({ + 'action': 'MFRC522_Read_start', + 'blockAddr': blockAddr, + 'command': f'0x{self.PCD_TRANSCEIVE:02X}' # Log command as hex value + }) + + (status, backData, backLen) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, + recvData) if not (status == self.MI_OK): - self.logger.error("Error while reading!") - + self.logger.error({ + 'error': "Error while reading!", + 'status': f'0x{status:02X}', # Log status as hex value + 'blockAddr': blockAddr + }) + + # Log completion with status as hex value + if self.log_verbose: + self.logger.debug({ + 'action': 'MFRC522_Read_complete', + 'status': f'0x{status:02X}', # Log status as hex value + 'blockAddr': blockAddr, + 'success': status == self.MI_OK + }) + if len(backData) == 16: - self.logger.debug("Sector " + str(blockAddr) + " " + str(backData)) + if self.log_verbose: + self.logger.debug({ + 'action': 'MFRC522_Read_data', + 'blockAddr': blockAddr, + 'data': str(backData) + }) return backData else: return None @@ -380,14 +500,39 @@ def MFRC522_Write(self, blockAddr, writeData): buff = [] buff.append(self.PICC_WRITE) buff.append(blockAddr) + + # Log start of write operation + if self.log_verbose: + self.logger.debug({ + 'action': 'MFRC522_Write_start', + 'blockAddr': blockAddr, + 'command': f'0x{self.PCD_TRANSCEIVE:02X}', # Log command as hex value + 'write_command': f'0x{self.PICC_WRITE:02X}' # Log write command as hex value + }) + crc = self.CalulateCRC(buff) buff.append(crc[0]) buff.append(crc[1]) - (status, backData, backLen) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, buff) - if not (status == self.MI_OK) or not (backLen == 4) or not ((backData[0] & 0x0F) == 0x0A): + (status, backData, backLen) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, + buff) + if not (status == self.MI_OK) or not (backLen == 4) or not ( + (backData[0] & 0x0F) == 0x0A): status = self.MI_ERR - - self.logger.debug("%s backdata &0x0F == 0x0A %s" % (backLen, backData[0] & 0x0F)) + self.logger.error({ + 'action': 'MFRC522_Write_first_phase_failed', + 'status': f'0x{status:02X}', # Log status as hex value + 'backLen': backLen, + 'backData': str(backData) if backData else "None" + }) + + if self.log_verbose: + self.logger.debug({ + 'action': 'MFRC522_Write_first_phase', + 'backLen': backLen, + 'backData_check': f'0x{backData[0] & 0x0F:02X}', # Log as hex value + 'status': f'0x{status:02X}' # Log status as hex value + }) + if status == self.MI_OK: buf = [] for i in range(16): @@ -396,12 +541,24 @@ def MFRC522_Write(self, blockAddr, writeData): crc = self.CalulateCRC(buf) buf.append(crc[0]) buf.append(crc[1]) - (status, backData, backLen) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, buf) - if not (status == self.MI_OK) or not (backLen == 4) or not ((backData[0] & 0x0F) == 0x0A): - self.logger.error("Error while writing") + (status, backData, backLen) = self.MFRC522_ToCard( + self.PCD_TRANSCEIVE, buf) + if not (status == self.MI_OK) or not (backLen == 4) or not ( + (backData[0] & 0x0F) == 0x0A): + self.logger.error({ + 'error': "Error while writing", + 'status': f'0x{status:02X}', # Log status as hex value + 'backLen': backLen, + 'backData': str(backData) if backData else "None" + }) if status == self.MI_OK: - self.logger.debug("Data written") - + if self.log_verbose: + self.logger.debug({ + 'action': 'MFRC522_Write_complete', + 'status': f'0x{status:02X}', # Log status as hex value + 'blockAddr': blockAddr, + 'success': True + }) def MFRC522_DumpClassic1K(self, key, uid): for i in range(64): @@ -410,7 +567,11 @@ def MFRC522_DumpClassic1K(self, key, uid): if status == self.MI_OK: self.MFRC522_Read(i) else: - self.logger.error("Authentication error") + self.logger.error({ + 'error': "Authentication error", + 'status': f'0x{status:02X}', # Log status as hex value + 'block': i + }) def MFRC522_Init(self): self.MFRC522_Reset() diff --git a/mfrc522/SimpleMFRC522.py b/mfrc522/SimpleMFRC522.py index 91eca06..903aee6 100644 --- a/mfrc522/SimpleMFRC522.py +++ b/mfrc522/SimpleMFRC522.py @@ -1,90 +1,213 @@ # Code by Simon Monk https://github.com/simonmonk/ +import logging +import time + from . import MFRC522 import RPi.GPIO as GPIO - +import sys + + class SimpleMFRC522: + DEFAULT_KEY = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF] + + _log = None + _mfrc522 = None + _key = None + + def __init__(self, key=None, pin_mode=GPIO.BOARD): + self._log = logging.getLogger(self.__class__.__name__) + + if key is None: + key = SimpleMFRC522.DEFAULT_KEY + self._key = key + self._mfrc522 = MFRC522(log_verbose=self._log.isEnabledFor(logging.DEBUG), + pin_mode=pin_mode) + + def read_id(self, attempts=sys.maxsize): + id = self.read_id_no_block() + tries = 1 + while not id and tries < attempts: + id = self.read_id_no_block() + tries += 1 + return id, tries + + def read_id_no_block(self): + reqMode = self._mfrc522.PICC_REQIDL + (status, TagType) = self._mfrc522.MFRC522_Request(reqMode) + if status != self._mfrc522.MI_OK: + # This error indicates no card present + self._log.debug({ + 'action': 'read_id_no_block_request_failed', + 'reqMode': f'0x{reqMode:02X}', # Log reqMode as hex value + 'status': f'0x{status:02X}' # Log status as hex value + }) + return None + (status, uid) = self._mfrc522.MFRC522_Anticoll() + if status != self._mfrc522.MI_OK: + self._log.error({ + 'action': 'read_id_no_block_anticoll_failed', + 'status': f'0x{status:02X}' # Log status as hex value + }) + return None + return self.uid_to_num(uid) + + def read(self, trailer=11, blocks=(8, 9, 10), attempts=sys.maxsize): + id, text = self.read_no_block(trailer=trailer, blocks=blocks) + tries = 1 + while not id and tries < attempts: + id, text = self.read_no_block(trailer=trailer, blocks=blocks) + tries += 1 + return id, text, tries + + def log_time(self, action, start): + if self._log.isEnabledFor(logging.DEBUG): + end = time.time() + self._log.debug({ + 'action': action, + 'start': f'{start:.5f}', + 'end': f'{end:.5f}', + 'duration': f'{end - start:.5f}', + }) + + def log_error_with_time(self, error, status, start): + if self._log.isEnabledFor(logging.DEBUG): + end = time.time() + self._log.debug({ + 'error': error, + 'status': f'0x{status:02X}', # Log status as hex value + 'start': f'{start:.5f}', + 'end': f'{end:.5f}', + 'duration': f'{end - start:.5f}', + }) + + def read_no_block(self, trailer=11, blocks=(8, 9, 10)): + start = time.time() + reqMode = self._mfrc522.PICC_REQIDL + status, _ = self._mfrc522.MFRC522_Request(reqMode) + self.log_time('MFRC522_Request', start) + if status != self._mfrc522.MI_OK: + # This error indicates no card present, no need for error logging + if self._log.isEnabledFor(logging.DEBUG): + self._log.error({ + 'action': 'MFRC522_Request_failed', + 'reqMode': f'0x{reqMode:02X}', # Log reqMode as hex value + 'status': f'0x{status:02X}' # Log status as hex value + }) + return None, None + + if self._log.isEnabledFor(logging.DEBUG): + self._log.debug({ + 'action': 'MFRC522_Request_Success', + 'reqMode': f'0x{reqMode:02X}', # Log reqMode as hex value + 'status': f'0x{status:02X}' # Log status as hex value + }) + + start = time.time() + status, uid = self._mfrc522.MFRC522_Anticoll() + if status != self._mfrc522.MI_OK: + self.log_error_with_time('MFRC522_Anticoll', status, start) + return None, None + self.log_time('MFRC522_Anticoll', start) + + start = time.time() + status, _ = self._mfrc522.MFRC522_SelectTag(uid) + if status != self._mfrc522.MI_OK: + self.log_error_with_time('MFRC522_SelectTag', status, start) + return None, None + self.log_time('MFRC522_SelectTag', start) + + start = time.time() + command = self._mfrc522.PICC_AUTHENT1A + status = self._mfrc522.MFRC522_Auth(command, + trailer, self._key, uid) + if status != self._mfrc522.MI_OK: + self.log_error_with_time('MFRC522_Auth', status, start) + self._log.error({ + 'action': 'MFRC522_Auth_Failed', + 'command': f'0x{command:02X}', # Log command as hex value + 'status': f'0x{status:02X}' # Log status as hex value + }) + return None, None + self.log_time('MFRC522_Auth', start) + if self._log.isEnabledFor(logging.DEBUG): + self._log.debug({ + 'action': 'MFRC522_Auth_Success', + 'command': f'0x{command:02X}', # Log command as hex value + 'status': f'0x{status:02X}' # Log status as hex value + }) + + data = [] + text_read = '' + if status == self._mfrc522.MI_OK: + for block_num in blocks: + start = time.time() + block = self._mfrc522.MFRC522_Read(block_num) + self.log_time('MFRC522_Read', start) + if block: + data += block + if data: + text_read = ''.join(chr(i) for i in data) + + start = time.time() + self._mfrc522.MFRC522_StopCrypto1() + self.log_time('MFRC522_StopCrypto1', start) + + id = self.uid_to_num(uid) + return id, text_read + + def write(self, text, trailer=11, blocks=(8, 9, 10), attempts=sys.maxsize): + id, text_out = self.write_no_block( + text, trailer=trailer, blocks=blocks) + tries = 1 + while not id and tries < attempts: + id, text_out = self.write_no_block( + text, trailer=trailer, blocks=blocks) + tries += 1 + return id, text_out, tries + + def write_no_block(self, text, trailer=11, blocks=(8, 9, 10)): + reqMode = self._mfrc522.PICC_REQIDL + (status, TagType) = self._mfrc522.MFRC522_Request(reqMode) + if status != self._mfrc522.MI_OK: + self._log.error({ + 'action': 'write_no_block_request_failed', + 'reqMode': f'0x{reqMode:02X}', # Log reqMode as hex value + 'status': f'0x{status:02X}' # Log status as hex value + }) + return None, None + (status, uid) = self._mfrc522.MFRC522_Anticoll() + if status != self._mfrc522.MI_OK: + self._log.error({ + 'action': 'write_no_block_anticoll_failed', + 'status': f'0x{status:02X}' # Log status as hex value + }) + return None, None + id = self.uid_to_num(uid) + self._mfrc522.MFRC522_SelectTag(uid) + command = self._mfrc522.PICC_AUTHENT1A + status = self._mfrc522.MFRC522_Auth( + command, trailer, self._key, uid) + if self._log.isEnabledFor(logging.DEBUG): + self._log.debug({ + 'action': 'write_no_block_auth', + 'command': f'0x{command:02X}', # Log command as hex value + 'status': f'0x{status:02X}' # Log status as hex value + }) + self._mfrc522.MFRC522_Read(trailer) + if status == self._mfrc522.MI_OK: + data = bytearray() + data.extend(bytearray(text.ljust(len(blocks) * 16).encode('ascii'))) + i = 0 + for block_num in blocks: + self._mfrc522.MFRC522_Write(block_num, + data[(i * 16):(i + 1) * 16]) + i += 1 + self._mfrc522.MFRC522_StopCrypto1() + return id, text[0:(len(blocks) * 16)] - READER = None - - KEY = [0xFF,0xFF,0xFF,0xFF,0xFF,0xFF] - BLOCK_ADDRS = [8, 9, 10] - - def __init__(self): - self.READER = MFRC522() - - def read(self): - id, text = self.read_no_block() - while not id: - id, text = self.read_no_block() - return id, text - - def read_id(self): - id = self.read_id_no_block() - while not id: - id = self.read_id_no_block() - return id - - def read_id_no_block(self): - (status, TagType) = self.READER.MFRC522_Request(self.READER.PICC_REQIDL) - if status != self.READER.MI_OK: - return None - (status, uid) = self.READER.MFRC522_Anticoll() - if status != self.READER.MI_OK: - return None - return self.uid_to_num(uid) - - def read_no_block(self): - (status, TagType) = self.READER.MFRC522_Request(self.READER.PICC_REQIDL) - if status != self.READER.MI_OK: - return None, None - (status, uid) = self.READER.MFRC522_Anticoll() - if status != self.READER.MI_OK: - return None, None - id = self.uid_to_num(uid) - self.READER.MFRC522_SelectTag(uid) - status = self.READER.MFRC522_Auth(self.READER.PICC_AUTHENT1A, 11, self.KEY, uid) - data = [] - text_read = '' - if status == self.READER.MI_OK: - for block_num in self.BLOCK_ADDRS: - block = self.READER.MFRC522_Read(block_num) - if block: - data += block - if data: - text_read = ''.join(chr(i) for i in data) - self.READER.MFRC522_StopCrypto1() - return id, text_read - - def write(self, text): - id, text_in = self.write_no_block(text) - while not id: - id, text_in = self.write_no_block(text) - return id, text_in - - def write_no_block(self, text): - (status, TagType) = self.READER.MFRC522_Request(self.READER.PICC_REQIDL) - if status != self.READER.MI_OK: - return None, None - (status, uid) = self.READER.MFRC522_Anticoll() - if status != self.READER.MI_OK: - return None, None - id = self.uid_to_num(uid) - self.READER.MFRC522_SelectTag(uid) - status = self.READER.MFRC522_Auth(self.READER.PICC_AUTHENT1A, 11, self.KEY, uid) - self.READER.MFRC522_Read(11) - if status == self.READER.MI_OK: - data = bytearray() - data.extend(bytearray(text.ljust(len(self.BLOCK_ADDRS) * 16).encode('ascii'))) - i = 0 - for block_num in self.BLOCK_ADDRS: - self.READER.MFRC522_Write(block_num, data[(i*16):(i+1)*16]) - i += 1 - self.READER.MFRC522_StopCrypto1() - return id, text[0:(len(self.BLOCK_ADDRS) * 16)] - - def uid_to_num(self, uid): - n = 0 - for i in range(0, 5): - n = n * 256 + uid[i] - return n + def uid_to_num(self, uid): + n = 0 + for i in range(0, 5): + n = n * 256 + uid[i] + return n diff --git a/setup.py b/setup.py index eaeea22..a59e839 100644 --- a/setup.py +++ b/setup.py @@ -5,10 +5,11 @@ setuptools.setup( name="mfrc522", - version="0.0.7", + version="0.0.9", author="Pi My Life Up", author_email="support@pimylifeup.com", - description="A library to integrate the MFRC522 RFID readers with the Raspberry Pi", + description="A library to integrate the MFRC522 RFID readers " + "with the Raspberry Pi", long_description=long_description, long_description_content_type="text/markdown", url="https://github.com/pimylifeup/MFRC522-python", @@ -16,7 +17,7 @@ install_requires=[ 'RPi.GPIO', 'spidev' - ], + ], classifiers=[ "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3",