├── .gitignore ├── LICENSE ├── README.md ├── deploy_esp.sh ├── deploy_wipy.sh ├── examples ├── read.py └── write.py └── mfrc522.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | # PyBuilder 59 | target/ 60 | 61 | #Ipython Notebook 62 | .ipynb_checkpoints 63 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Stefan Wendler 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # micropython-mfrc522 2 | (Micro)Python class to access the MFRC522 RFID reader 3 | 4 | Basic class to access RFID readers of the type [MFRC522](http://www.nxp.com/documents/data_sheet/MFRC522.pdf). 5 | This is basically a re-write of [this](https://github.com/mxgxw/MFRC522-python) Python port for the MFRC522. I 6 | tried to strip things down and make them more "pythonic" so the result is small enough to run on 7 | [Micropython](https://github.com/micropython/micropython) boards. I tried the class so far on the 8 | [ESP8266](https://github.com/micropython/micropython/tree/master/esp8266) and 9 | the [WiPy](https://github.com/micropython/micropython/tree/master/cc3200). 10 | 11 | ## Usage 12 | 13 | Put the modules ``mfrc522.py``, ``examples/read.py``, ``examples/write.py`` to the root of the flash FS on your board. 14 | For the ESP8266 there are multiple solutions to do that. E.g. use the 15 | [WebREPL file transfer](https://github.com/micropython/webrepl), or [mpfshell](https://github.com/wendlers/mpfshell). 16 | 17 | I used the following pins for my setup: 18 | 19 | | Signal | GPIO ESP32 | Note | 20 | | --------- | ------------ | ------------------------------------ | 21 | | sck | 18 | | 22 | | mosi | 23 | | 23 | | miso | 19 | | 24 | | cs | 5 |Labeled SDA on most RFID-RC522 boards | 25 | 26 | Now enter the REPL you could run one of the two exmaples: 27 | 28 | For detecting, authenticating and reading from a card: 29 | 30 | import read 31 | read.do_read() 32 | 33 | This will wait for a MifareClassic 1k card. As soon the card is detected, it is authenticated, and 34 | 16 bytes are read from address 0x08. 35 | 36 | For detecting, authenticating and writing to a card: 37 | 38 | import write 39 | write.do_write() 40 | 41 | This will wait for a MifareClassic 1k card. As soon the card is detected, it is authenticated, and 42 | 16 bytes written to address 0x08. 43 | -------------------------------------------------------------------------------- /deploy_esp.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | mpfshell -c "open ttyUSB0; put mfrc522.py; lcd examples; put read.py; put write.py; ls" -n 4 | -------------------------------------------------------------------------------- /deploy_wipy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | mpfshell -c "open ttyUSB0; cd flash/lib; put mfrc522.py; lcd examples; put read.py; put write.py; ls" -n 4 | -------------------------------------------------------------------------------- /examples/read.py: -------------------------------------------------------------------------------- 1 | from time import sleep_ms 2 | from machine import Pin, SPI 3 | from lib.rfid.mfrc522 import MFRC522 4 | 5 | sck = Pin(18, Pin.OUT) 6 | mosi = Pin(23, Pin.OUT) 7 | miso = Pin(19, Pin.OUT) 8 | spi = SPI(baudrate=100000, polarity=0, phase=0, sck=sck, mosi=mosi, miso=miso) 9 | 10 | sda = Pin(5, Pin.OUT) 11 | 12 | def do_read(): 13 | try: 14 | while True: 15 | rdr = MFRC522(spi, sda) 16 | uid = "" 17 | (stat, tag_type) = rdr.request(rdr.REQIDL) 18 | if stat == rdr.OK: 19 | (stat, raw_uid) = rdr.anticoll() 20 | if stat == rdr.OK: 21 | uid = ("0x%02x%02x%02x%02x" % (raw_uid[0], raw_uid[1], raw_uid[2], raw_uid[3])) 22 | print(uid) 23 | sleep_ms(100) 24 | except KeyboardInterrupt: 25 | print("Bye") 26 | -------------------------------------------------------------------------------- /examples/write.py: -------------------------------------------------------------------------------- 1 | import mfrc522 2 | from machine import Pin, SPI 3 | sck = Pin(18, Pin.OUT) 4 | mosi = Pin(23, Pin.OUT) 5 | miso = Pin(19, Pin.OUT) 6 | spi = SPI(baudrate=100000, polarity=0, phase=0, sck=sck, mosi=mosi, miso=miso) 7 | 8 | sda = Pin(5, Pin.OUT) 9 | 10 | def do_write(): 11 | rdr = MFRC522(spi, sda) 12 | 13 | print("") 14 | print("Place card before reader to write address 0x08") 15 | print("") 16 | 17 | try: 18 | while True: 19 | 20 | (stat, tag_type) = rdr.request(rdr.REQIDL) 21 | 22 | if stat == rdr.OK: 23 | 24 | (stat, raw_uid) = rdr.anticoll() 25 | 26 | if stat == rdr.OK: 27 | print("New card detected") 28 | print(" - tag type: 0x%02x" % tag_type) 29 | print(" - uid : 0x%02x%02x%02x%02x" % (raw_uid[0], raw_uid[1], raw_uid[2], raw_uid[3])) 30 | print("") 31 | 32 | if rdr.select_tag(raw_uid) == rdr.OK: 33 | 34 | key = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF] 35 | 36 | if rdr.auth(rdr.AUTHENT1A, 8, key, raw_uid) == rdr.OK: 37 | stat = rdr.write(8, b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f") 38 | rdr.stop_crypto1() 39 | if stat == rdr.OK: 40 | print("Data written to card") 41 | else: 42 | print("Failed to write data to card") 43 | else: 44 | print("Authentication error") 45 | else: 46 | print("Failed to select tag") 47 | 48 | except KeyboardInterrupt: 49 | print("Bye") 50 | -------------------------------------------------------------------------------- /mfrc522.py: -------------------------------------------------------------------------------- 1 | from machine import Pin, SPI 2 | from os import uname 3 | 4 | 5 | class MFRC522: 6 | 7 | OK = 0 8 | NOTAGERR = 1 9 | ERR = 2 10 | 11 | REQIDL = 0x26 12 | REQALL = 0x52 13 | AUTHENT1A = 0x60 14 | AUTHENT1B = 0x61 15 | 16 | def __init__(self, spi, cs): 17 | 18 | self.spi = spi 19 | self.cs = cs 20 | self.cs.value(1) 21 | self.spi.init() 22 | self.init() 23 | 24 | def _wreg(self, reg, val): 25 | 26 | self.cs.value(0) 27 | self.spi.write(b'%c' % int(0xff & ((reg << 1) & 0x7e))) 28 | self.spi.write(b'%c' % int(0xff & val)) 29 | self.cs.value(1) 30 | 31 | def _rreg(self, reg): 32 | 33 | self.cs.value(0) 34 | self.spi.write(b'%c' % int(0xff & (((reg << 1) & 0x7e) | 0x80))) 35 | val = self.spi.read(1) 36 | self.cs.value(1) 37 | 38 | return val[0] 39 | 40 | def _sflags(self, reg, mask): 41 | self._wreg(reg, self._rreg(reg) | mask) 42 | 43 | def _cflags(self, reg, mask): 44 | self._wreg(reg, self._rreg(reg) & (~mask)) 45 | 46 | def _tocard(self, cmd, send): 47 | 48 | recv = [] 49 | bits = irq_en = wait_irq = n = 0 50 | stat = self.ERR 51 | 52 | if cmd == 0x0E: 53 | irq_en = 0x12 54 | wait_irq = 0x10 55 | elif cmd == 0x0C: 56 | irq_en = 0x77 57 | wait_irq = 0x30 58 | 59 | self._wreg(0x02, irq_en | 0x80) 60 | self._cflags(0x04, 0x80) 61 | self._sflags(0x0A, 0x80) 62 | self._wreg(0x01, 0x00) 63 | 64 | for c in send: 65 | self._wreg(0x09, c) 66 | self._wreg(0x01, cmd) 67 | 68 | if cmd == 0x0C: 69 | self._sflags(0x0D, 0x80) 70 | 71 | i = 2000 72 | while True: 73 | n = self._rreg(0x04) 74 | i -= 1 75 | if ~((i != 0) and ~(n & 0x01) and ~(n & wait_irq)): 76 | break 77 | 78 | self._cflags(0x0D, 0x80) 79 | 80 | if i: 81 | if (self._rreg(0x06) & 0x1B) == 0x00: 82 | stat = self.OK 83 | 84 | if n & irq_en & 0x01: 85 | stat = self.NOTAGERR 86 | elif cmd == 0x0C: 87 | n = self._rreg(0x0A) 88 | lbits = self._rreg(0x0C) & 0x07 89 | if lbits != 0: 90 | bits = (n - 1) * 8 + lbits 91 | else: 92 | bits = n * 8 93 | 94 | if n == 0: 95 | n = 1 96 | elif n > 16: 97 | n = 16 98 | 99 | for _ in range(n): 100 | recv.append(self._rreg(0x09)) 101 | else: 102 | stat = self.ERR 103 | 104 | return stat, recv, bits 105 | 106 | def _crc(self, data): 107 | 108 | self._cflags(0x05, 0x04) 109 | self._sflags(0x0A, 0x80) 110 | 111 | for c in data: 112 | self._wreg(0x09, c) 113 | 114 | self._wreg(0x01, 0x03) 115 | 116 | i = 0xFF 117 | while True: 118 | n = self._rreg(0x05) 119 | i -= 1 120 | if not ((i != 0) and not (n & 0x04)): 121 | break 122 | 123 | return [self._rreg(0x22), self._rreg(0x21)] 124 | 125 | def init(self): 126 | 127 | self.reset() 128 | self._wreg(0x2A, 0x8D) 129 | self._wreg(0x2B, 0x3E) 130 | self._wreg(0x2D, 30) 131 | self._wreg(0x2C, 0) 132 | self._wreg(0x15, 0x40) 133 | self._wreg(0x11, 0x3D) 134 | self.antenna_on() 135 | 136 | def reset(self): 137 | self._wreg(0x01, 0x0F) 138 | 139 | def antenna_on(self, on=True): 140 | 141 | if on and ~(self._rreg(0x14) & 0x03): 142 | self._sflags(0x14, 0x03) 143 | else: 144 | self._cflags(0x14, 0x03) 145 | 146 | def request(self, mode): 147 | 148 | self._wreg(0x0D, 0x07) 149 | (stat, recv, bits) = self._tocard(0x0C, [mode]) 150 | 151 | if (stat != self.OK) | (bits != 0x10): 152 | stat = self.ERR 153 | 154 | return stat, bits 155 | 156 | def anticoll(self): 157 | 158 | ser_chk = 0 159 | ser = [0x93, 0x20] 160 | 161 | self._wreg(0x0D, 0x00) 162 | (stat, recv, bits) = self._tocard(0x0C, ser) 163 | 164 | if stat == self.OK: 165 | if len(recv) == 5: 166 | for i in range(4): 167 | ser_chk = ser_chk ^ recv[i] 168 | if ser_chk != recv[4]: 169 | stat = self.ERR 170 | else: 171 | stat = self.ERR 172 | 173 | return stat, recv 174 | 175 | def select_tag(self, ser): 176 | 177 | buf = [0x93, 0x70] + ser[:5] 178 | buf += self._crc(buf) 179 | (stat, recv, bits) = self._tocard(0x0C, buf) 180 | return self.OK if (stat == self.OK) and (bits == 0x18) else self.ERR 181 | 182 | def auth(self, mode, addr, sect, ser): 183 | return self._tocard(0x0E, [mode, addr] + sect + ser[:4])[0] 184 | 185 | def stop_crypto1(self): 186 | self._cflags(0x08, 0x08) 187 | 188 | def read(self, addr): 189 | 190 | data = [0x30, addr] 191 | data += self._crc(data) 192 | (stat, recv, _) = self._tocard(0x0C, data) 193 | return recv if stat == self.OK else None 194 | 195 | def write(self, addr, data): 196 | 197 | buf = [0xA0, addr] 198 | buf += self._crc(buf) 199 | (stat, recv, bits) = self._tocard(0x0C, buf) 200 | 201 | if not (stat == self.OK) or not (bits == 4) or not ((recv[0] & 0x0F) == 0x0A): 202 | stat = self.ERR 203 | else: 204 | buf = [] 205 | for i in range(16): 206 | buf.append(data[i]) 207 | buf += self._crc(buf) 208 | (stat, recv, bits) = self._tocard(0x0C, buf) 209 | if not (stat == self.OK) or not (bits == 4) or not ((recv[0] & 0x0F) == 0x0A): 210 | stat = self.ERR 211 | 212 | return stat 213 | --------------------------------------------------------------------------------