RFID sensor

MIFARE

MIFARE is the NXP Semiconductors-owned trademark of a series of chips widely used in contactless smart cards and proximity cards.

First deployed in 1994, MIFARE ICs were originally developed for automated fare collection in public transport, but that was just the beginning. Since then, MIFARE continues to pave the way how millions of people around the world access services, and enables contactless transit, payment, and access experiences for everyone, independent of location and time. https://www.mifare.net/en/

The MFRC522 reader

The MFRC522 is a highly integrated reader/writer for contactless communication at 13.56 MHz. The MFRC522 reader supports ISO 14443A / MIFARE® mode.

Chipset

DIGITAL Interfaces

Various host interfaces are implemented:

  • SPI interface
  • serial UART (similar to RS232 with voltage levels according pad voltage supply)
  • I2C interface

SPI Compatible interface

An interface compatible to an SPI interface enables a high-speed serial communication between the MFRC522 and a μ-Controller for the communication. The implemented SPI compatible interface is according to a standard SPI interface.

The MFRC522 acts as a slave during the SPI communication. The SPI clock SCK has to be generated by the master. Data communication from the master to the slave uses the Line MOSI. Line MISO is used to send data back from the MFRC522 to the master.

UART Interface

The internal UART interface is compatible to an RS232 serial interface. The default transfer speed is 9.6 kbit/s.

I2C Bus Interface

An Inter IC (I2C) bus interface is supported to enable a low cost, low pin count serial bus interface to the host. The implemented I2C interface is implemented according the NXP Semiconductors I2C interface specification, rev. 2.1, January 2000.

The implemented interface can only act in Slave mode.

Therefore no clock generation and access arbitration is implemented in the MFRC522. SDA is a bi-directional line, connected to a positive supply voltage via a current-source or a pull-up resistor. Both lines SDA and SCL are set to HIGH level if no data is transmitted. Data on the I2C-bus can be transferred at data rates of up to 100 kbit/s in Standard mode, up to 400 kbit/s in the Fast mode or up to 3.4 Mbit/s in the High-speed mode.

FIFO Buffer

An 64 × 8-bit FIFO buffer is implemented in the MFRC522. It buffers the input and output data stream between the host and the internal state machine of the MFRC522. Thus, it is possible to handle data streams with lengths of up to 64 bytes without taking timing constraints into account.

MFRC522 Command Set

The behavior is determined by a state machine capable to perform a certain set of commands. By writing the according command-code to register CommandReg the command is executed. Arguments and/or data necessary to process a command are exchanged via the FIFO buffer.

Command Command code Action
Idle 0000 No action; cancels current command execution.
Mem 0001 Stores 25 byte into the internal buffer
Generate RandomID 0010 Generates a 10 byte random ID number
CalcCRC 0011 Activates the CRC co-processor or performs a selftest.
Transmit 0100 Transmits data from the FIFO buffer.
NoCmd Change 0111 No command change. This command can be used to modify different bits in the command register without touching the command. E.g. Power-down.
Receive 1000 Activates the receiver circuitry.
Transceive 1100 Transmits data from FIFO buffer to the antenna and activates automatically the receiver after transmission.
. 1101 Reserved for further use
MFAuthent 1110 Performs the MIFARE® standard authentication as a reader
Soft Reset 1111 Resets the MFRC522

The Card

ISO 14443A Card

ISO/IEC 14443 Identification cards -- Contactless integrated circuit cards -- Proximity cards is an international standard that defines proximity cards used for identification, and the transmission protocols for communicating with it.

Parts:

  • ISO/IEC 14443-1:2016 Part 1: Physical characteristics
  • ISO/IEC 14443-2:2016 Part 2: Radio frequency power and signal interface
  • ISO/IEC 14443-3:2016 Part 3: Initialization and anticollision
  • ISO/IEC 14443-4:2016 Part 4: Transmission protocol

Types

Cards may be Type A and Type B, both of which communicate via radio at 13.56 MHz (RFID HF). The main differences between these types concern modulation methods, coding schemes (Part 2) and protocol initialization procedures (Part 3). Both Type A and Type B cards use the same transmission protocol (described in Part 4). The transmission protocol specifies data block exchange and related mechanisms:

  • data block chaining
  • waiting time extension
  • multi-activation

ISO/IEC 14443 uses following terms for components:

  • PCD: proximity coupling device (the card reader)
  • PICC: proximity integrated circuit card

Notable implementations

  • MIFARE cards (partial or full implementation, depending on product)
  • Biometric passports
  • EMV payment cards (PayPass, Visa payWave, ExpressPay)
  • National identity cards in the European Economic Area
  • Near Field Communication is based on in part, and is compatible with, ISO/IEC 14443
  • Calypso, open security standard for transit fare collection systems
  • CIPURSE, open security standard for transit fare collection systems

source https://en.wikipedia.org/wiki/ISO/IEC_14443

MIFARE Cards

The MIFARE brand name (derived from the term MIKRON FARE Collection and created by the company MIKRON) covers four families of contactless cards:

  • MIFARE Classic - Employs a proprietary protocol compliant to parts 1–3 of ISO/IEC 14443 Type A, with an NXP proprietary security protocol for authentication and ciphering. Subtype: MIFARE Classic EV1 (other subtypes are no longer in use).
  • MIFARE Plus - Drop-in replacement for MIFARE Classic with certified security level (AES-128 based) and is fully backwards compatible with MIFARE Classic. Subtypes MIFARE Plus S, MIFARE Plus X and MIFARE Plus SE.
  • MIFARE Ultralight - Low-cost ICs that are useful for high volume applications such as public transport, loyalty cards and event ticketing. Subtypes: MIFARE Ultralight C, MIFARE Ultralight EV1 and MIFARE Ultralight Nano.
  • MIFARE DESFire - Contactless ICs that comply to parts 3 and 4 of ISO/IEC 14443-4 Type A with a mask-ROM operating system from NXP. The DES in the name refers to the use of a DES, two-key 3DES, three-key 3DES and AES encryption; while Fire is an acronym for Fast, innovative, reliable, and enhanced. Subtypes: MIFARE DESFire EV1, MIFARE DESFire EV2.

Raspberry Pi integration

Overview

the wiring and the coding

Wiring

The RFID RC522 is a very low-cost RFID (Radio-frequency identification) reader and writer that is based on the MFRC522 microcontroller. This microcontroller provides its data through the SPI protocol and works by creating a 13.56MHz electromagnetic field that it uses to communicate with the RFID tags.

On your RFID RC522 you will notice that there are 8 possible connections on it, these being

  • SDA (Serial Data Signal),
  • SCK (Serial Clock),
  • MOSI (Master Out Slave In),
  • MISO (Master In Slave Out),
  • IRQ (Interrupt Request),
  • GND (Ground Power),
  • RST (Reset-Circuit)
  • and 3.3v (3.3v Power In).

Proposed connection

RFID RC522 GPIO
SDA Pin 24
SCK Pin 23
MOSI Pin 19
MISO Pin 21
IRQ Not connected
GND Pin 6
RST Pin 22
3.3V Pin 1

Source https://pimylifeup.com/raspberry-pi-rfid-rc522/

The code

SPI python library not available by default

Reference https://github.com/simonmonk/clever_card_kit

Enable the SPI

see the documentation https://www.raspberrypi.org/documentation/hardware/raspberrypi/spi/README.md

Python Library

python2.7-dev

sudo apt-get install python2.7-dev

SPI-Py

cd ~
git clone https://github.com/lthiery/SPI-Py.git
cd ~/SPI-Py
sudo python setup.py install

Writing with the RFID RC522

cd ~
git clone https://github.com/pimylifeup/MFRC522-python.git
#!/usr/bin/env python

import RPi.GPIO as GPIO
import SimpleMFRC522

reader = SimpleMFRC522.SimpleMFRC522()

try:
        text = raw_input('New data:')
        print("Now place your tag to write")
        reader.write(text)
        print("Written")
finally:
        GPIO.cleanup()

SimpleMFRC522.py

# Code by Simon Monk https://github.com/simonmonk/

import MFRC522
import RPi.GPIO as GPIO

class SimpleMFRC522:

  READER = None;

  KEY = [0xFF,0xFF,0xFF,0xFF,0xFF,0xFF]
  BLOCK_ADDRS = [8, 9, 10]

  def __init__(self):
    self.READER = MFRC522.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, None
      (status, uid) = self.READER.MFRC522_Anticoll()
      if status != self.READER.MI_OK:
          return None, 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

MFRC522.py

# Modifications made by Simon Monk https://github.com/simonmonk/
# Modified from: https://github.com/mxgxw/MFRC522-python/blob/master/MFRC522.py
# Trace commented out and the Read and Write methods modified to return values.
# Also changed to use the Broadcom pin mode

import RPi.GPIO as GPIO
import spi
import signal
import time

class MFRC522:
  NRSTPD = 25

  MAX_LEN = 16

  PCD_IDLE       = 0x00
  PCD_AUTHENT    = 0x0E
  PCD_RECEIVE    = 0x08
  PCD_TRANSMIT   = 0x04
  PCD_TRANSCEIVE = 0x0C
  PCD_RESETPHASE = 0x0F
  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

  MI_OK       = 0
  MI_NOTAGERR = 1
  MI_ERR      = 2

  Reserved00     = 0x00
  CommandReg     = 0x01
  CommIEnReg     = 0x02
  DivlEnReg      = 0x03
  CommIrqReg     = 0x04
  DivIrqReg      = 0x05
  ErrorReg       = 0x06
  Status1Reg     = 0x07
  Status2Reg     = 0x08
  FIFODataReg    = 0x09
  FIFOLevelReg   = 0x0A
  WaterLevelReg  = 0x0B
  ControlReg     = 0x0C
  BitFramingReg  = 0x0D
  CollReg        = 0x0E
  Reserved01     = 0x0F

  Reserved10     = 0x10
  ModeReg        = 0x11
  TxModeReg      = 0x12
  RxModeReg      = 0x13
  TxControlReg   = 0x14
  TxAutoReg      = 0x15
  TxSelReg       = 0x16
  RxSelReg       = 0x17
  RxThresholdReg = 0x18
  DemodReg       = 0x19
  Reserved11     = 0x1A
  Reserved12     = 0x1B
  MifareReg      = 0x1C
  Reserved13     = 0x1D
  Reserved14     = 0x1E
  SerialSpeedReg = 0x1F

  Reserved20        = 0x20  
  CRCResultRegM     = 0x21
  CRCResultRegL     = 0x22
  Reserved21        = 0x23
  ModWidthReg       = 0x24
  Reserved22        = 0x25
  RFCfgReg          = 0x26
  GsNReg            = 0x27
  CWGsPReg          = 0x28
  ModGsPReg         = 0x29
  TModeReg          = 0x2A
  TPrescalerReg     = 0x2B
  TReloadRegH       = 0x2C
  TReloadRegL       = 0x2D
  TCounterValueRegH = 0x2E
  TCounterValueRegL = 0x2F

  Reserved30      = 0x30
  TestSel1Reg     = 0x31
  TestSel2Reg     = 0x32
  TestPinEnReg    = 0x33
  TestPinValueReg = 0x34
  TestBusReg      = 0x35
  AutoTestReg     = 0x36
  VersionReg      = 0x37
  AnalogTestReg   = 0x38
  TestDAC1Reg     = 0x39
  TestDAC2Reg     = 0x3A
  TestADCReg      = 0x3B
  Reserved31      = 0x3C
  Reserved32      = 0x3D
  Reserved33      = 0x3E
  Reserved34      = 0x3F

  serNum = []

  def __init__(self, dev='/dev/spidev0.0', spd=1000000):
    spi.openSPI(device=dev,speed=spd)
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(25, GPIO.OUT)
    GPIO.output(self.NRSTPD, 1)
    self.MFRC522_Init()

  def MFRC522_Reset(self):
    self.Write_MFRC522(self.CommandReg, self.PCD_RESETPHASE)

  def Write_MFRC522(self, addr, val):
    spi.transfer(((addr<<1)&0x7E,val))

  def Read_MFRC522(self, addr):
    val = spi.transfer((((addr<<1)&0x7E) | 0x80,0))
    return val[1]

  def SetBitMask(self, reg, mask):
    tmp = self.Read_MFRC522(reg)
    self.Write_MFRC522(reg, tmp | mask)

  def ClearBitMask(self, reg, mask):
    tmp = self.Read_MFRC522(reg);
    self.Write_MFRC522(reg, tmp & (~mask))

  def AntennaOn(self):
    temp = self.Read_MFRC522(self.TxControlReg)
    if(~(temp & 0x03)):
      self.SetBitMask(self.TxControlReg, 0x03)

  def AntennaOff(self):
    self.ClearBitMask(self.TxControlReg, 0x03)

  def MFRC522_ToCard(self,command,sendData):
    backData = []
    backLen = 0
    status = self.MI_ERR
    irqEn = 0x00
    waitIRq = 0x00
    lastBits = None
    n = 0
    i = 0

    if command == self.PCD_AUTHENT:
      irqEn = 0x12
      waitIRq = 0x10
    if command == self.PCD_TRANSCEIVE:
      irqEn = 0x77
      waitIRq = 0x30

    self.Write_MFRC522(self.CommIEnReg, irqEn|0x80)
    self.ClearBitMask(self.CommIrqReg, 0x80)
    self.SetBitMask(self.FIFOLevelReg, 0x80)

    self.Write_MFRC522(self.CommandReg, self.PCD_IDLE);  

    while(i<len(sendData)):
      self.Write_MFRC522(self.FIFODataReg, sendData[i])
      i = i+1

    self.Write_MFRC522(self.CommandReg, command)

    if command == self.PCD_TRANSCEIVE:
      self.SetBitMask(self.BitFramingReg, 0x80)

    i = 2000
    while True:
      n = self.Read_MFRC522(self.CommIrqReg)
      i = i - 1
      if ~((i!=0) and ~(n&0x01) and ~(n&waitIRq)):
        break

    self.ClearBitMask(self.BitFramingReg, 0x80)

    if i != 0:
      if (self.Read_MFRC522(self.ErrorReg) & 0x1B)==0x00:
        status = self.MI_OK

        if n & irqEn & 0x01:
          status = self.MI_NOTAGERR

        if command == self.PCD_TRANSCEIVE:
          n = self.Read_MFRC522(self.FIFOLevelReg)
          lastBits = self.Read_MFRC522(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:
            backData.append(self.Read_MFRC522(self.FIFODataReg))
            i = i + 1;
      else:
        status = self.MI_ERR

    return (status,backData,backLen)

  def MFRC522_Request(self, reqMode):
    status = None
    backBits = None
    TagType = []

    self.Write_MFRC522(self.BitFramingReg, 0x07)

    TagType.append(reqMode);
    (status,backData,backBits) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, TagType)

    if ((status != self.MI_OK) | (backBits != 0x10)):
      status = self.MI_ERR

    return (status,backBits)

  def MFRC522_Anticoll(self):
    backData = []
    serNumCheck = 0

    serNum = []

    self.Write_MFRC522(self.BitFramingReg, 0x00)

    serNum.append(self.PICC_ANTICOLL)
    serNum.append(0x20)

    (status,backData,backBits) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE,serNum)

    if(status == self.MI_OK):
      i = 0
      if len(backData)==5:
        while i<4:
          serNumCheck = serNumCheck ^ backData[i]
          i = i + 1
        if serNumCheck != backData[i]:
          status = self.MI_ERR
      else:
        status = self.MI_ERR

    return (status,backData)

  def CalulateCRC(self, pIndata):
    self.ClearBitMask(self.DivIrqReg, 0x04)
    self.SetBitMask(self.FIFOLevelReg, 0x80);
    i = 0
    while i<len(pIndata):
      self.Write_MFRC522(self.FIFODataReg, pIndata[i])
      i = i + 1
    self.Write_MFRC522(self.CommandReg, self.PCD_CALCCRC)
    i = 0xFF
    while True:
      n = self.Read_MFRC522(self.DivIrqReg)
      i = i - 1
      if not ((i != 0) and not (n&0x04)):
        break
    pOutData = []
    pOutData.append(self.Read_MFRC522(self.CRCResultRegL))
    pOutData.append(self.Read_MFRC522(self.CRCResultRegM))
    return pOutData

  def MFRC522_SelectTag(self, serNum):
    backData = []
    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, backData, backLen) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, buf)

    if (status == self.MI_OK) and (backLen == 0x18):
      #print "Size: " + str(backData[0])
      return    backData[0]
    else:
      return 0

  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, backData, backLen) = self.MFRC522_ToCard(self.PCD_AUTHENT,buff)

    # Check if an error occurred
    if not(status == self.MI_OK):
      print("AUTH ERROR!!")
    if not (self.Read_MFRC522(self.Status2Reg) & 0x08) != 0:
      print("AUTH ERROR(status2reg & 0x08) != 0")

    # Return the status
    return status

  def MFRC522_StopCrypto1(self):
    self.ClearBitMask(self.Status2Reg, 0x08)

  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, backData, backLen) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, recvData)
    if not(status == self.MI_OK):
      print("Error while reading!")
    i = 0
    if len(backData) == 16:
      return backData
    else:
      return None

  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, 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

    #print str(backLen)+" backdata &0x0F == 0x0A "+str(backData[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, 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):
            print("Error while writing")
        # if status == self.MI_OK:
        #     print "Data written"

  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

  def MFRC522_Init(self):
    GPIO.output(self.NRSTPD, 1)

    self.MFRC522_Reset();

    self.Write_MFRC522(self.TModeReg, 0x8D)
    self.Write_MFRC522(self.TPrescalerReg, 0x3E)
    self.Write_MFRC522(self.TReloadRegL, 30)
    self.Write_MFRC522(self.TReloadRegH, 0)

    self.Write_MFRC522(self.TxAutoReg, 0x40)
    self.Write_MFRC522(self.ModeReg, 0x3D)
self.AntennaOn()

Reading with the RFID RC522

#!/usr/bin/env python

import RPi.GPIO as GPIO
import SimpleMFRC522

reader = SimpleMFRC522.SimpleMFRC522()

try:
        id, text = reader.read()
        print(id)
        print(text)
finally:
        GPIO.cleanup()