SPICOMPOSANT

RC522 — Lecteur RFID

Lecteur de badges sans contact 13,56 MHz (Mifare). Chaque badge a un identifiant unique (UID) qui permet de reconnaître les personnes — parfait pour le contrôle d’accès et les escape games.

Comment ça marche

  1. Le RC522 émet un champ radio à 13,56 MHz
  2. Quand un badge Mifare entre dans le champ (~3 cm), il s’alimente par induction (sans pile)
  3. Le badge renvoie son UID (identifiant unique, 4 octets) : ex. DE:AD:BE:EF
  4. Le programme compare l’UID à une liste autorisée
  5. Décision : accès accordé ou refusé

Chaque badge a un UID gravé en usine — impossible à modifier.


Câblage

⚠️

Le RC522 fonctionne en 3,3V uniquement

Ne JAMAIS brancher le VCC du RC522 sur le 5V — le module grille instantanément.

Module RC522ESP32Notes
3.3V3.3V3,3V uniquement
GNDGND
SCKD18 (GPIO18)Horloge SPI
MOSID23 (GPIO23)Données ESP32 → RC522
MISOD19 (GPIO19)Données RC522 → ESP32
SDA / NSSD5 (GPIO5)Chip Select (CS) — marqué SDA ou NSS selon les cartes
RSTD22 (GPIO22)Reset
IRQnon connectéOptionnel

Installation de la bibliothèque

Le fichier mfrc522.py est inclus dans le dossier composant sur GitHub.

  1. Télécharger mfrc522.py depuis le dossier composants/rc522-rfid/
  2. Dans Thonny : clic droit sur le fichier → Upload to /
mfrc522.py — à copier sur l'ESP32 .python
composants/rc522-rfid/mfrc522.py
285 lignes GitHub
# Bibliothèque MFRC522 pour lecteur RFID RC522
# Source : cefn/micropython-mfrc522 (MIT License)
# https://github.com/cefn/micropython-mfrc522
#
# À copier sur l'ESP32 via Thonny (clic droit → Upload to /)

from machine import Pin, SPI
from os import uname

emptyRecv = b""

class MFRC522:

    GAIN_REG = 0x26
    MAX_GAIN = 0x07

    OK = 0
    NOTAGERR = 1
    ERR = 2

    REQIDL = 0x26
    REQALL = 0x52
    AUTHENT1A = 0x60
    AUTHENT1B = 0x61

    def __init__(self, spi=None, gpioRst=None, gpioCs=None):

        if gpioRst is not None:
            self.rst = Pin(gpioRst, Pin.OUT)
        else:
            self.rst = None
        assert(gpioCs is not None, "Needs gpioCs")
        if gpioCs is not None:
            self.cs = Pin(gpioCs, Pin.OUT)
        else:
            self.cs = None

        self.regBuf = bytearray(4)
        self.blockWriteBuf = bytearray(18)
        self.authBuf = bytearray(12)
        self.wregBuf = bytearray(2)
        self.rregBuf = bytearray(1)
        self.recvBuf = bytearray(16)
        self.recvMv = memoryview(self.recvBuf)

        if self.rst is not None:
            self.rst.value(0)
        if self.cs is not None:
            self.cs.value(1)

        if spi is not None:
            self.spi = spi
        else:
            sck = Pin(14, Pin.OUT)
            mosi = Pin(13, Pin.OUT)
            miso = Pin(12, Pin.IN)
            if uname()[0] == 'WiPy':
                self.spi = SPI(0)
                self.spi.init(SPI.MASTER, baudrate=1000000, pins=(sck, mosi, miso))
            elif uname()[0] == 'esp8266':
                self.spi = SPI(baudrate=100000, polarity=0, phase=0, sck=sck, mosi=mosi, miso=miso)
                self.spi.init()
            else:
                raise RuntimeError("Unsupported platform")

        if self.rst is not None:
            self.rst.value(1)
        self.init()

    def _wreg(self, reg, val):
        if self.cs is not None:
            self.cs.value(0)
        buf = self.wregBuf
        buf[0] = 0xff & ((reg << 1) & 0x7e)
        buf[1] = 0xff & val
        self.spi.write(buf)
        if self.cs is not None:
            self.cs.value(1)

    def _rreg(self, reg):
        if self.cs is not None:
            self.cs.value(0)
        buf = self.rregBuf
        buf[0] = 0xff & (((reg << 1) & 0x7e) | 0x80)
        self.spi.write(buf)
        val = self.spi.read(1)
        if self.cs is not None:
            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, into=None):
        recv = emptyRecv
        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

                    if into is None:
                        recv = self.recvBuf
                    else:
                        recv = into
                    pos = 0
                    while pos < n:
                        recv[pos] = self._rreg(0x09)
                        pos += 1
                    if into is None:
                        recv = self.recvMv[:n]
                    else:
                        recv = into
            else:
                stat = self.ERR

        return stat, recv, bits

    def _assign_crc(self, data, count):
        self._cflags(0x05, 0x04)
        self._sflags(0x0A, 0x80)

        dataPos = 0
        while dataPos < count:
            self._wreg(0x09, data[dataPos])
            dataPos += 1

        self._wreg(0x01, 0x03)

        i = 0xFF
        while True:
            n = self._rreg(0x05)
            i -= 1
            if not ((i != 0) and not (n & 0x04)):
                break

        data[count] = self._rreg(0x22)
        data[count + 1] = 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.set_gain(self.MAX_GAIN)
        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):
        ser_chk = 0
        ser = [0x93, 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, bytearray(recv)

    def select_tag(self, ser):
        buf = bytearray(9)
        buf[0] = 0x93
        buf[1] = 0x70
        buf[2:7] = ser
        self._assign_crc(buf, 7)
        (stat, recv, bits) = self._tocard(0x0C, buf)
        return self.OK if (stat == self.OK) and (bits == 0x18) else self.ERR

    def auth(self, mode, addr, sect, ser):
        buf = self.authBuf
        buf[0] = mode
        buf[1] = addr
        buf[2:8] = sect
        buf[8:12] = ser[:4]
        return self._tocard(0x0E, buf)[0]

    def stop_crypto1(self):
        self._cflags(0x08, 0x08)

    def set_gain(self, gain):
        assert gain <= self.MAX_GAIN
        self._cflags(self.GAIN_REG, 0x07 << 4)
        self._sflags(self.GAIN_REG, gain << 4)

    def read(self, addr, into=None):
        buf = self.regBuf
        buf[0] = 0x30
        buf[1] = addr
        self._assign_crc(buf, 2)
        (stat, recv, _) = self._tocard(0x0C, buf, into=into)
        if into is None:
            recv = bytearray(recv)
        return recv if stat == self.OK else None

    def write(self, addr, data):
        buf = self.regBuf
        buf[0] = 0xA0
        buf[1] = addr
        self._assign_crc(buf, 2)
        (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 = self.blockWriteBuf
            i = 0
            while i < 16:
                buf[i] = data[i]
                i += 1
            self._assign_crc(buf, 16)
            (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

Utilisation rapide

>>> from machine import Pin, SPI
>>> from mfrc522 import MFRC522
>>> spi = SPI(1, baudrate=1000000, sck=Pin(18), mosi=Pin(23), miso=Pin(19))
>>> rfid = MFRC522(spi, gpioRst=22, gpioCs=5)
>>> # Présenter un badge, puis :
>>> stat, tag = rfid.request(rfid.REQIDL)
>>> stat, uid = rfid.anticoll()
>>> print(':'.join(f'{b:02X}' for b in uid))
DE:AD:BE:EF

Test de lecture

Lit les badges présentés devant le lecteur et affiche leur UID. Utile pour vérifier le câblage.

test_rfid.py — vérifier que le module fonctionne .python
composants/rc522-rfid/test_rfid.py
71 lignes GitHub
# Test rapide — Lecteur RFID RC522 (Joy-it) sur ESP32
# -----------------------------------------------------------------------
# Lit l'UID des badges présentés devant le lecteur et l'affiche.
# Permet de vérifier que le câblage fonctionne et de découvrir
# les identifiants de vos badges.
#
# Câblage ESP32 → RC522 :
#   D18  → SCK         D23  → MOSI       D19  → MISO
#   D5   → SDA (CS)    D22  → RST
#   3.3V → 3.3V        GND  → GND
#
#   ⚠ JAMAIS brancher le RC522 en 5V — il grille !
#
# Installation de la bibliothèque (une seule fois) :
#   Copier mfrc522.py sur l'ESP32 via Thonny (clic droit → Upload to /)
#   Le fichier est dans le même dossier que ce script.
# -----------------------------------------------------------------------

from machine import Pin, SPI
import time

# Importer la bibliothèque RFID
try:
    from mfrc522 import MFRC522
except ImportError:
    print("Bibliothèque mfrc522 non installée !")
    print("Copier mfrc522.py sur l'ESP32 via Thonny (clic droit → Upload to /)")
    raise SystemExit

# Initialisation SPI + lecteur RFID
spi  = SPI(1, baudrate=1000000, sck=Pin(18), mosi=Pin(23), miso=Pin(19))
rfid = MFRC522(spi, gpioRst=22, gpioCs=5)

print("Lecteur RFID RC522 prêt")
print("Présentez un badge devant le lecteur...")
print("Ctrl+C pour arrêter")
print()

badges_vus = set()

while True:
    # Chercher un badge à proximité
    stat, tag_type = rfid.request(rfid.REQIDL)

    if stat == rfid.OK:
        # Un badge est détecté — lire son UID (identifiant unique)
        stat, uid = rfid.anticoll()

        if stat == rfid.OK:
            # Convertir l'UID en texte lisible : "AA:BB:CC:DD"
            uid_str = ':'.join(f'{b:02X}' for b in uid)

            if uid_str not in badges_vus:
                badges_vus.add(uid_str)
                print(f"Badge détecté : {uid_str}")
            else:
                print(f"  (déjà vu)    : {uid_str}")

    time.sleep_ms(200)

# -----------------------------------------------------------------------
# DANS LE SHELL après Ctrl+C :
#   >>> rfid.request(rfid.REQIDL)     # chercher un badge
#   >>> rfid.anticoll()                # lire l'UID
#
# CHAQUE BADGE A UN UID UNIQUE (4 octets en hexadécimal).
# C'est ce qui permet de distinguer les personnes :
#   "DE:AD:BE:EF" → Vincent
#   "CA:FE:BA:BE" → Marie
# -----------------------------------------------------------------------

Aller plus loin

Pour construire un vrai projet avec le RC522, voir l’atelier Badge RFID qui propose un système de contrôle d’accès complet avec 3 niveaux (Visiteur, Membre, Admin) :