diff --git a/.gitignore b/.gitignore index 1343767..52aec4f 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,41 @@ # IDEA files .idea/ +cmake-build-*/ + +# Results for the checking of the Python coding style and static analysis +.mypy_cache +flake8_output.txt + +# ESP-IDF default build directory name +build + +# lock files for examples and components +dependencies.lock + +# managed_components for examples +managed_components + +# pytest log +pytest_embedded_log/ +list_job*.txt +size_info*.txt +XUNIT_RESULT*.xml + +# clang config (for LSP) +.clangd + +# Vale +.vale/styles/* + +# pio +.pio + +# sqlite +*.db + +.env + +# vinscant +key.txt +webrepl_cli.py diff --git a/vinscant/mpfiles/lib/mfrc522.py b/vinscant/mpfiles/lib/mfrc522.py new file mode 100644 index 0000000..a7e05e7 --- /dev/null +++ b/vinscant/mpfiles/lib/mfrc522.py @@ -0,0 +1,451 @@ +# From https://github.com/danjperron/micropython-mfrc522/ + +from machine import Pin, SPI +from os import uname + +class MFRC522: + DEBUG = False + OK = 0 + NOTAGERR = 1 + ERR = 2 + + NTAG_213 = 213 + NTAG_215 = 215 + NTAG_216 = 216 + NTAG_NONE = 0 + + REQIDL = 0x26 + REQALL = 0x52 + AUTHENT1A = 0x60 + AUTHENT1B = 0x61 + + PICC_ANTICOLL1 = 0x93 + PICC_ANTICOLL2 = 0x95 + PICC_ANTICOLL3 = 0x97 + + def __init__(self, sck, mosi, miso, rst, cs,baudrate=1000000,spi_id=0): + self.sck = Pin(sck, Pin.OUT) + self.mosi = Pin(mosi, Pin.OUT) + self.miso = Pin(miso) + self.rst = Pin(rst, Pin.OUT) + self.cs = Pin(cs, Pin.OUT) + + self.rst.value(0) + self.cs.value(1) + self.NTAG = 0 + self.NTAG_MaxPage = 0 + board = uname()[0] + + if board == 'WiPy' or board == 'LoPy' or board == 'FiPy': + self.spi = SPI(0) + self.spi.init(SPI.MASTER, baudrate=1000000, pins=(self.sck, self.mosi, self.miso)) + elif (board == 'esp8266') or (board == 'esp32'): + self.spi = SPI(baudrate=100000, polarity=0, phase=0, sck=self.sck, mosi=self.mosi, miso=self.miso) + self.spi.init() + elif board == 'rp2': + self.spi = SPI(spi_id,baudrate=baudrate,sck=self.sck, mosi= self.mosi, miso= self.miso) + else: + raise RuntimeError("Unsupported platform") + + self.rst.value(1) + self.init() + + def _wreg(self, reg, val): + self.cs.value(0) + self.spi.write(b'%c' % int(0xff & ((reg << 1) & 0x7e))) + self.spi.write(b'%c' % int(0xff & val)) + self.cs.value(1) + + def _rreg(self, reg): + self.cs.value(0) + self.spi.write(b'%c' % int(0xff & (((reg << 1) & 0x7e) | 0x80))) + val = self.spi.read(1) + self.cs.value(1) + + return val[0] + + def _sflags(self, reg, mask): + self._wreg(reg, self._rreg(reg) | mask) + + def _cflags(self, reg, mask): + self._wreg(reg, self._rreg(reg) & (~mask)) + + def _tocard(self, cmd, send): + recv = [] + bits = irq_en = wait_irq = n = 0 + stat = self.ERR + + if cmd == 0x0E: + irq_en = 0x12 + wait_irq = 0x10 + elif cmd == 0x0C: + irq_en = 0x77 + wait_irq = 0x30 + + self._wreg(0x02, irq_en | 0x80) + self._cflags(0x04, 0x80) + self._sflags(0x0A, 0x80) + self._wreg(0x01, 0x00) + + for c in send: + self._wreg(0x09, c) + self._wreg(0x01, cmd) + + if cmd == 0x0C: + self._sflags(0x0D, 0x80) + + i = 2000 + while True: + n = self._rreg(0x04) + i -= 1 + if ~((i != 0) and ~(n & 0x01) and ~(n & wait_irq)): + break + + self._cflags(0x0D, 0x80) + + if i: + if (self._rreg(0x06) & 0x1B) == 0x00: + stat = self.OK + + if n & irq_en & 0x01: + stat = self.NOTAGERR + elif cmd == 0x0C: + n = self._rreg(0x0A) + lbits = self._rreg(0x0C) & 0x07 + if lbits != 0: + bits = (n - 1) * 8 + lbits + else: + bits = n * 8 + + if n == 0: + n = 1 + elif n > 16: + n = 16 + + for _ in range(n): + recv.append(self._rreg(0x09)) + else: + stat = self.ERR + + return stat, recv, bits + + def _crc(self, data): + self._cflags(0x05, 0x04) + self._sflags(0x0A, 0x80) + + for c in data: + self._wreg(0x09, c) + + self._wreg(0x01, 0x03) + + i = 0xFF + while True: + n = self._rreg(0x05) + i -= 1 + if not ((i != 0) and not (n & 0x04)): + break + + return [self._rreg(0x22), self._rreg(0x21)] + + def init(self): + self.reset() + self._wreg(0x2A, 0x8D) + self._wreg(0x2B, 0x3E) + self._wreg(0x2D, 30) + self._wreg(0x2C, 0) + self._wreg(0x15, 0x40) + self._wreg(0x11, 0x3D) + self.antenna_on() + + def reset(self): + self._wreg(0x01, 0x0F) + + def antenna_on(self, on=True): + if on and ~(self._rreg(0x14) & 0x03): + self._sflags(0x14, 0x03) + else: + self._cflags(0x14, 0x03) + + def request(self, mode): + self._wreg(0x0D, 0x07) + (stat, recv, bits) = self._tocard(0x0C, [mode]) + + if (stat != self.OK) | (bits != 0x10): + stat = self.ERR + + return stat, bits + + def anticoll(self,anticolN): + ser_chk = 0 + ser = [anticolN, 0x20] + + self._wreg(0x0D, 0x00) + (stat, recv, bits) = self._tocard(0x0C, ser) + + if stat == self.OK: + if len(recv) == 5: + for i in range(4): + ser_chk = ser_chk ^ recv[i] + if ser_chk != recv[4]: + stat = self.ERR + else: + stat = self.ERR + + return stat, recv + + def PcdSelect(self, serNum,anticolN): + backData = [] + buf = [] + buf.append(anticolN) + buf.append(0x70) + #i = 0 + ###xorsum=0; + for i in serNum: + buf.append(i) + #while i<5: + # buf.append(serNum[i]) + # i = i + 1 + pOut = self._crc(buf) + buf.append(pOut[0]) + buf.append(pOut[1]) + (status, backData, backLen) = self._tocard( 0x0C, buf) + if (status == self.OK) and (backLen == 0x18): + return 1 + else: + return 0 + + + def SelectTag(self, uid): + byte5 = 0 + + #(status,puid)= self.anticoll(self.PICC_ANTICOLL1) + #print("uid",uid,"puid",puid) + for i in uid: + byte5 = byte5 ^ i + puid = uid + [byte5] + + if self.PcdSelect(puid,self.PICC_ANTICOLL1) == 0: + return (self.ERR,[]) + return (self.OK , uid) + + def tohexstring(self,v): + s="[" + for i in v: + if i != v[0]: + s = s+ ", " + s=s+ "0x{:02X}".format(i) + s= s+ "]" + return s + + def SelectTagSN(self): + valid_uid=[] + (status,uid)= self.anticoll(self.PICC_ANTICOLL1) + #print("Select Tag 1:",self.tohexstring(uid)) + if status != self.OK: + return (self.ERR,[]) + + if self.DEBUG: print("anticol(1) {}".format(uid)) + if self.PcdSelect(uid,self.PICC_ANTICOLL1) == 0: + return (self.ERR,[]) + if self.DEBUG: print("pcdSelect(1) {}".format(uid)) + + #check if first byte is 0x88 + if uid[0] == 0x88 : + #ok we have another type of card + valid_uid.extend(uid[1:4]) + (status,uid)=self.anticoll(self.PICC_ANTICOLL2) + #print("Select Tag 2:",self.tohexstring(uid)) + if status != self.OK: + return (self.ERR,[]) + if self.DEBUG: print("Anticol(2) {}".format(uid)) + rtn = self.PcdSelect(uid,self.PICC_ANTICOLL2) + if self.DEBUG: print("pcdSelect(2) return={} uid={}".format(rtn,uid)) + if rtn == 0: + return (self.ERR,[]) + if self.DEBUG: print("PcdSelect2() {}".format(uid)) + #now check again if uid[0] is 0x88 + if uid[0] == 0x88 : + valid_uid.extend(uid[1:4]) + (status , uid) = self.anticoll(self.PICC_ANTICOLL3) + #print("Select Tag 3:",self.tohexstring(uid)) + if status != self.OK: + return (self.ERR,[]) + if self.DEBUG: print("Anticol(3) {}".format(uid)) + if self.PcdSelect(uid,self.PICC_ANTICOLL3) == 0: + return (self.ERR,[]) + if self.DEBUG: print("PcdSelect(3) {}".format(uid)) + valid_uid.extend(uid[0:5]) + # if we are here than the uid is ok + # let's remove the last BYTE whic is the XOR sum + + return (self.OK , valid_uid[:len(valid_uid)-1]) + #return (self.OK , valid_uid) + + def auth(self, mode, addr, sect, ser): + return self._tocard(0x0E, [mode, addr] + sect + ser[:4])[0] + + def authKeys(self,uid,addr,keyA=None, keyB=None): + status = self.ERR + if keyA is not None: + status = self.auth(self.AUTHENT1A, addr, keyA, uid) + elif keyB is not None: + status = self.auth(self.AUTHENT1B, addr, keyB, uid) + return status + + def stop_crypto1(self): + self._cflags(0x08, 0x08) + + def read(self, addr): + data = [0x30, addr] + data += self._crc(data) + (stat, recv, _) = self._tocard(0x0C, data) + return stat, recv + + def write(self, addr, data): + buf = [0xA0, addr] + buf += self._crc(buf) + (stat, recv, bits) = self._tocard(0x0C, buf) + + if not (stat == self.OK) or not (bits == 4) or not ((recv[0] & 0x0F) == 0x0A): + stat = self.ERR + else: + buf = [] + for i in range(16): + buf.append(data[i]) + buf += self._crc(buf) + (stat, recv, bits) = self._tocard(0x0C, buf) + if not (stat == self.OK) or not (bits == 4) or not ((recv[0] & 0x0F) == 0x0A): + stat = self.ERR + return stat + + + def writeSectorBlock(self,uid, sector, block, data, keyA=None, keyB = None): + absoluteBlock = sector * 4 + (block % 4) + if absoluteBlock > 63 : + return self.ERR + if len(data) != 16: + return self.ERR + if self.authKeys(uid,absoluteBlock,keyA,keyB) != self.ERR : + return self.write(absoluteBlock, data) + return self.ERR + + def readSectorBlock(self,uid ,sector, block, keyA=None, keyB = None): + absoluteBlock = sector * 4 + (block % 4) + if absoluteBlock > 63 : + return self.ERR, None + if self.authKeys(uid,absoluteBlock,keyA,keyB) != self.ERR : + return self.read(absoluteBlock) + return self.ERR, None + + def MFRC522_DumpClassic1K(self,uid, Start=0, End=64, keyA=None, keyB=None): + for absoluteBlock in range(Start,End): + status = self.authKeys(uid,absoluteBlock,keyA,keyB) + # Check if authenticated + print("{:02d} S{:02d} B{:1d}: ".format(absoluteBlock, absoluteBlock//4 , absoluteBlock % 4),end="") + if status == self.OK: + status, block = self.read(absoluteBlock) + if status == self.ERR: + break + else: + for value in block: + print("{:02X} ".format(value),end="") + print(" ",end="") + for value in block: + if (value > 0x20) and (value < 0x7f): + print(chr(value),end="") + else: + print('.',end="") + print("") + else: + break + if status == self.ERR: + print("Authentication error") + return self.ERR + return self.OK + + def MFRC522_Dump_NTAG(self,Start=0, End=135): + for absoluteBlock in range(Start,End,4): + MaxIndex = 4 * 135 + status = self.OK + print("Page {:02d}: ".format(absoluteBlock),end="") + if status == self.OK: + status, block = self.read(absoluteBlock) + if status == self.ERR: + break + else: + Index = absoluteBlock*4 + for i in range(len(block)): + if Index < MaxIndex : + print("{:02X} ".format(block[i]),end="") + else: + print(" ",end="") + if (i%4)==3: + print(" ",end="") + Index+=1 + print(" ",end="") + Index = absoluteBlock*4 + for value in block: + if Index < MaxIndex: + if (value > 0x20) and (value < 0x7f): + print(chr(value),end="") + else: + print('.',end="") + Index+=1 + print("") + else: + break + if status == self.ERR: + print("Authentication error") + return self.ERR + return self.OK + + def writeNTAGPage(self,page,data): + if page>self.NTAG_MaxPage: + return self.ERR + if page < 4: + return self.ERR + if len(data) != 4: + return self.ERR + + return self.write(page,data+[0]*12) + + def getNTAGVersion(self): + buf = [0x60] + buf += self._crc(buf) + stat, recv,_ = self._tocard(0x0C, buf) + return stat, recv + + #Version NTAG213 = [0x0 ,0x4, 0x4, 0x2, 0x1, 0x0,0x0f, 0x3] + #Version NTAG215 = [0x0 ,0x4, 0x4, 0x2, 0x1, 0x0,0x11, 0x3] + #Version NTAG216 = [0x0 ,0x4, 0x4, 0x2, 0x1, 0x0,0x13, 0x3] + + def IsNTAG(self): + self.NTAG = self.NTAG_NONE + self.NTAG_MaxPage=0 + (stat , rcv) = self.getNTAGVersion() + if stat == self.OK: + if len(rcv) < 8: + return False #do we have at least 8 bytes + if rcv[0] != 0: + return False #check header + if rcv[1] != 4: + return False #check Vendor ID + if rcv[2] != 4: + return False #check product type + if rcv[3] != 2: + return False #check subtype + if rcv[7] != 3: + return False #check protocol + if rcv[6] == 0xf: + self.NTAG= self.NTAG_213 + self.NTAG_MaxPage = 44 + return True + if rcv[6] == 0x11: + self.NTAG= self.NTAG_215 + self.NTAG_MaxPage = 134 + return True + if rcv[7] == 0x13: + self.NTAG= self.NTAG_216 + self.NTAG_MaxPage = 230 + return True + return False