RFID LabRFID

Lecteur RFID RC522

Lisez un badge, comprenez son UID et ses blocs, puis construisez un contrôle d’accès. Le laboratoire vous montre ce que le lecteur voit avant que vous ne câbliez quoi que ce soit.

  1. Comprendre
  2. Voir
  3. Manipuler
  4. Programmer
  5. Résoudre
  6. Construire

En bref

PuceNXP MFRC522, lecteur et écrivain 13,56 MHz (ISO/IEC 14443 A, cartes MIFARE)
InterfaceSPI sur les modules courants (la puce sait aussi faire I²C et UART)
Alimentation3,3 V (2,5 à 3,3 V). Ne branche jamais le VCC sur 5 V
Portéequelques centimètres, selon l’antenne et le badge
Cartes luesMIFARE Classic 1K/4K, MIFARE Ultralight, NTAG (autocollants et porte-clés NFC)
Souvent vendu avecune carte blanche et un porte-clés MIFARE Classic 1K

13,56 MHz, pas 125 kHz. Les badges à 125 kHz (EM4100) ne sont pas lus par le RC522. Il faut un lecteur 125 kHz, comme le RDM6300.

Ce guide vous fait passer du premier badge lu à un contrôle d’accès complet, avec un badge virtuel pour comprendre ce que le lecteur voit avant même de câbler.

Câblage

Broche RC522ESP32 (30 broches)Arduino Uno / Nano
SDA (SS / CS)GPIO 510
SCKGPIO 1813
MOSIGPIO 2311
MISOGPIO 1912
IRQnon connectéenon connectée
GNDGNDGND
RSTGPIO 229
3.3V3V33,3 V
  • Le RC522 se branche en 3,3 V. Sur une Uno ou une Nano, utilise la sortie 3,3 V (elle fournit assez de courant : la puce consomme une vingtaine de milliampères).
  • Sur une Uno, les lignes SPI sont au niveau 5 V : ajoutez un diviseur ou un convertisseur de niveau si vous voulez protéger la puce.
  • La broche IRQ reste libre : la bibliothèque interroge le lecteur, elle n’a pas besoin de l’interruption.
  • Garde des fils courts (10 cm environ) pour le SPI, surtout au-dessus de 4 MHz.

Les GPIO 21 et 22 sont le bus I²C par défaut de l’ESP32. Si vous ajoutez un écran OLED, placez RST ailleurs (le projet Badge Cloneur utilise le GPIO 27).

SPI

Le SPI utilise quatre fils : une horloge (SCK), une ligne du maître vers l’esclave (MOSI), une ligne de retour (MISO) et une ligne de sélection (SS, à l’état bas pour parler au RC522). Plusieurs périphériques peuvent partager SCK, MOSI et MISO ; chacun a son propre SS.

Bus ESP32SCKMISOMOSISS
VSPI (défaut)GPIO 18GPIO 19GPIO 23GPIO 5
HSPIGPIO 14GPIO 12GPIO 13GPIO 15

La bibliothèque MFRC522 utilise l’objet global SPI. Choisir « HSPI » revient donc à passer les broches HSPI à SPI.begin(SCK, MISO, MOSI, SS) ; le configurateur plus bas fait ce réglage pour vous. Le GPIO 12 (MISO du HSPI) est une broche de démarrage de l’ESP32 : si le montage la force à l’état haut au moment du reset, la carte peut refuser de démarrer.

Premier badge

Choisissez un badge virtuel : vous voyez ce que le lecteur en tire (UID, type, blocs) et ce que le moniteur série affiche avec rc522_uid_read.ino. Le résultat « autorisé » dépend de l’UID que vous déclarez.

Moniteur série (115 200 bauds)

UID
Taille de l’UID
Type (bibliothèque)
SAK / ATQA
Mémoire

Contenu lu
rc522_uid_read.inoTélécharger
/*
 * Premier badge — Kaihatsu Lab
 * Lecteur RC522 (SPI) sur ESP32 : affiche l'UID et le type de chaque carte présentée.
 * Bibliothèque : MFRC522 (gestionnaire de bibliothèques de l'IDE Arduino).
 */
#include <SPI.h>
#include <MFRC522.h>

// Broches ESP32 (VSPI). Adaptez-les à votre câblage.
const uint8_t PIN_SCK  = 18;
const uint8_t PIN_MISO = 19;
const uint8_t PIN_MOSI = 23;
const uint8_t PIN_SS   = 5;
const uint8_t PIN_RST  = 22;

MFRC522 rfid(PIN_SS, PIN_RST);

void setup() {
  Serial.begin(115200);
  SPI.begin(PIN_SCK, PIN_MISO, PIN_MOSI, PIN_SS);
  rfid.PCD_Init();
  delay(50);
  rfid.PCD_DumpVersionToSerial();       // 0x91 ou 0x92 : puce reconnue. 0x00 ou 0xFF : problème de câblage.
  Serial.println(F("Approche une carte du lecteur..."));
}

void loop() {
  if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) return;

  Serial.print(F("UID :"));
  for (byte i = 0; i < rfid.uid.size; i++) {
    Serial.print(rfid.uid.uidByte[i] < 0x10 ? " 0" : " ");
    Serial.print(rfid.uid.uidByte[i], HEX);
  }
  Serial.println();

  MFRC522::PICC_Type type = rfid.PICC_GetType(rfid.uid.sak);
  Serial.print(F("Type : "));
  Serial.println(rfid.PICC_GetTypeName(type));

  rfid.PICC_HaltA();                    // carte en veille : elle ne sera relue qu'après avoir été retirée puis représentée
  rfid.PCD_StopCrypto1();
}

Le lecteur répond « version 0x91 ou 0x92 » quand la puce est bien câblée. Toute autre valeur (0x00, 0xFF) signale un problème de câblage ou d’alimentation : voir le dépannage.

UID

L’UID (Unique IDentifier) est le numéro de série de la carte : 4, 7 ou 10 octets selon la famille. Les MIFARE Classic ont en général 4 octets ; les NTAG et les Ultralight en ont 7. Le lecteur le lit sans clé, à chaque présentation.

L’UID n’est pas un secret. Il se lit sans authentification et des cartes à UID modifiable existent. Pour un accès qui compte, utilise-le comme identifiant, pas comme mot de passe : ajoute une donnée secrète protégée par clé, ou un contrôle côté serveur.

Formateur d'UID

MIFARE

MIFARE Classic 1K
Mémoire1 024 octets (1K) ou 4 096 octets (4K)
Organisation16 secteurs de 4 blocs (1K), 16 octets par bloc
Bloc 0fabricant : UID, BCC, SAK, ATQA (lecture seule sur une carte normale)
Dernier bloc de chaque secteur« sector trailer » : clé A (6 octets), bits d’accès (4), clé B (6)
Clé d’usineFF FF FF FF FF FF
Famille Ultralight / NTAG
Ultralight, NTAG213pages de 4 octets (NTAG213 : 45 pages, soit 180 octets dont 144 utilisables)
Protectionpas de clés MIFARE Classic : mot de passe 32 bits en option (NTAG) ; le code d’écriture est différent

Carte des blocs (1K)

Clique sur un bloc.

Lecture

Pour lire un bloc de données d’une carte MIFARE Classic, il faut d’abord s’authentifier sur le secteur avec la clé A ou B, puis appeler MIFARE_Read. Une carte neuve accepte la clé d’usine. Le code suivant lit le bloc 4 et l’affiche en hexadécimal et en texte.

rc522_read_block.inoTélécharger
/*
 * Lecture d'un bloc MIFARE Classic — Kaihatsu Lab
 * Authentification avec la clé A d'usine (FF FF FF FF FF FF), puis lecture du bloc 4
 * affiché en hexadécimal et en texte.
 */
#include <SPI.h>
#include <MFRC522.h>

const uint8_t PIN_SS  = 5;
const uint8_t PIN_RST = 22;
const byte    BLOCK   = 4;              // secteur 1 : blocs 4, 5 et 6 pour les données, le 7 est la « trailer »

MFRC522 rfid(PIN_SS, PIN_RST);
MFRC522::MIFARE_Key key;

void setup() {
  Serial.begin(115200);
  SPI.begin(18, 19, 23, PIN_SS);        // SCK, MISO, MOSI, SS
  rfid.PCD_Init();
  for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF;   // clé d'usine
  Serial.println(F("Approche une carte MIFARE Classic..."));
}

void loop() {
  if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) return;

  MFRC522::StatusCode status = rfid.PCD_Authenticate(
      MFRC522::PICC_CMD_MF_AUTH_KEY_A, BLOCK, &key, &(rfid.uid));

  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("Authentification refusee : "));
    Serial.println(rfid.GetStatusCodeName(status));
  } else {
    byte buffer[18];                    // 16 octets de données + 2 octets de CRC
    byte size = sizeof(buffer);
    status = rfid.MIFARE_Read(BLOCK, buffer, &size);

    if (status == MFRC522::STATUS_OK) {
      Serial.print(F("Bloc "));
      Serial.print(BLOCK);
      Serial.print(F(" :"));
      for (byte i = 0; i < 16; i++) {
        Serial.print(buffer[i] < 0x10 ? " 0" : " ");
        Serial.print(buffer[i], HEX);
      }
      Serial.print(F("  | "));
      for (byte i = 0; i < 16; i++) {
        Serial.print(buffer[i] >= 32 && buffer[i] < 127 ? (char)buffer[i] : '.');
      }
      Serial.println();
    } else {
      Serial.print(F("Lecture impossible : "));
      Serial.println(rfid.GetStatusCodeName(status));
    }
  }

  rfid.PICC_HaltA();
  rfid.PCD_StopCrypto1();               // à appeler après toute authentification
}
  • MIFARE_Read renvoie 18 octets : 16 de données et 2 de CRC.
  • PCD_StopCrypto1() désactive le chiffrement après l’authentification. Sans lui, la carte suivante peut échouer.
  • Pour une carte Ultralight ou NTAG, il n’y a pas d’authentification : utilise MIFARE_Read sur une page, sans clé.

Écriture

Écrire suit le même chemin : authentification, puis MIFARE_Write avec exactement 16 octets. Ce code écrit « KAIHATSU LAB » dans le bloc 4 et relit le bloc pour vérifier.

Attention. N’écris jamais dans le bloc 0 ni dans un sector trailer (blocs 3, 7, 11, 15...). Une erreur dans le trailer peut rendre le secteur illisible pour toujours. Le code refuse ces blocs.

rc522_write_block.inoTélécharger
/*
 * Écriture d'un bloc MIFARE Classic — Kaihatsu Lab
 * Écrit un texte de 16 caractères maximum dans le bloc 4, puis relit le bloc pour vérifier.
 *
 * ATTENTION : n'écris jamais dans le bloc 0 ni dans un « sector trailer » (blocs 3, 7, 11, 15...).
 * Le trailer contient les clés et les droits d'accès : une erreur peut rendre le secteur définitivement illisible.
 */
#include <SPI.h>
#include <MFRC522.h>

const uint8_t PIN_SS  = 5;
const uint8_t PIN_RST = 22;
const byte    BLOCK   = 4;
const char    TEXT[]  = "KAIHATSU LAB";  // 16 caractères maximum

MFRC522 rfid(PIN_SS, PIN_RST);
MFRC522::MIFARE_Key key;

void setup() {
  Serial.begin(115200);
  SPI.begin(18, 19, 23, PIN_SS);
  rfid.PCD_Init();
  for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF;

  if (BLOCK == 0 || (BLOCK % 4) == 3) {
    Serial.println(F("Bloc interdit : choisis un bloc de donnees (4, 5, 6, 8, 9, 10...)."));
    while (true) delay(1000);
  }
  Serial.println(F("Approche une carte MIFARE Classic pour ecrire..."));
}

void loop() {
  if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) return;

  MFRC522::StatusCode status = rfid.PCD_Authenticate(
      MFRC522::PICC_CMD_MF_AUTH_KEY_A, BLOCK, &key, &(rfid.uid));

  if (status == MFRC522::STATUS_OK) {
    byte data[16] = {0};
    size_t n = strlen(TEXT);
    if (n > 16) n = 16;
    memcpy(data, TEXT, n);

    status = rfid.MIFARE_Write(BLOCK, data, 16);
    if (status == MFRC522::STATUS_OK) {
      byte check[18];
      byte size = sizeof(check);
      if (rfid.MIFARE_Read(BLOCK, check, &size) == MFRC522::STATUS_OK && memcmp(check, data, 16) == 0) {
        Serial.println(F("Ecriture verifiee."));
      } else {
        Serial.println(F("Ecriture faite, mais la relecture ne correspond pas."));
      }
    } else {
      Serial.print(F("Ecriture impossible : "));
      Serial.println(rfid.GetStatusCodeName(status));
    }
  } else {
    Serial.print(F("Authentification refusee : "));
    Serial.println(rfid.GetStatusCodeName(status));
  }

  rfid.PICC_HaltA();
  rfid.PCD_StopCrypto1();
}

Sur une NTAG ou une Ultralight, l’écriture se fait par page de 4 octets avec MIFARE_Ultralight_Write.

Authentification

Paramètre de PCD_AuthenticateValeur
Type de cléPICC_CMD_MF_AUTH_KEY_A ou PICC_CMD_MF_AUTH_KEY_B
Blocn’importe quel bloc du secteur visé : l’authentification vaut pour tout le secteur
Cléstructure MIFARE_Key de 6 octets
UIDl’UID de la carte, tel que lu (rfid.uid)
  • Chaque secteur a ses propres clés A et B, stockées dans son trailer. Un système d’accès sérieux les change et garde la clé B secrète.
  • Les bits d’accès décident ce que chaque clé peut faire (lire, écrire, incrémenter, changer les clés). La configuration d’usine autorise la clé A ou B à lire et écrire les blocs de données.
  • « Authentification refusée » veut presque toujours dire : la carte a déjà été configurée avec d’autres clés.

Personnalisation

Règle les broches, choisis le bus SPI, puis récupère un sketch prêt à téléverser.

Configurateur

rc522_config.ino
// Le code apparaît ici.

Dépannage

SymptômeCause probableSolution
Version 0x00 ou 0xFF au démarrageCâblage, alimentation ou SS/RST inversésVérifie 3,3 V et GND, puis SCK, MOSI, MISO et SS. Teste avec des fils courts.
Aucune carte détectéeBadge 125 kHz, carte trop loin ou antenne masquéeUtilise un badge 13,56 MHz (MIFARE, NTAG) à 1 ou 2 cm de l’antenne, à plat.
La carte n’est lue qu’une foisPICC_HaltA() l’a mise en veilleC’est voulu : retire la carte puis représente-la. Retire l’appel pour relire en continu.
Authentification refuséeClés différentes de celles d’usineUtilise les bonnes clés A ou B du secteur, ou essaie une carte neuve.
Écriture impossibleBloc 0, trailer ou droits d’accès restrictifsÉcris dans un bloc de données (4, 5, 6, 8...) d’un secteur en configuration d’usine.
L’UID change à chaque lectureTéléphone ou carte bancaire (UID aléatoire)Ces appareils masquent leur UID. Utilise un badge ou un autocollant NFC classique.
Redémarrages de l’ESP32 à la lectureAlimentation instableAlimente par une source correcte, ajoute un condensateur de 10 à 100 µF sur le 3,3 V du module.
Ne compile pasBibliothèque absente ou mauvaise carteInstalle « MFRC522 » et sélectionne bien la carte ESP32 dans le menu Outils.

Bibliothèques

BibliothèqueÀ retenirÀ choisir pour
MFRC522La bibliothèque de référence, très répandue, avec de nombreux exemples. Elle utilise l’objet global SPI.Tous les exemples de ce guide et le projet Badge Cloneur
MFRC522v2Réécriture qui sépare le pilote (SPI, I²C) de la logique. Plus souple pour plusieurs lecteurs.Plusieurs RC522 sur un même montage, ou SPI personnalisé

Les deux s’installent depuis le gestionnaire de bibliothèques de l’IDE Arduino. Ne les installe pas ensemble : elles partagent des noms de fichiers.

Projets

Projet interactif : serrure à badge

RC522, écran SSD1306 et servomoteur. Passe un badge virtuel : l’écran affiche le même texte que le vrai montage, et le servo tourne si l’UID est autorisé (ici 04 A3 7B 2C).

SERVO

Passer un badge :

En attente d’un badge.

rc522_oled_servo.inoTélécharger
/*
 * Serrure à badge — Kaihatsu Lab
 * RC522 (SPI) + écran OLED SSD1306 (I2C) + servomoteur sur ESP32.
 * Un badge autorisé affiche « ACCES OK » et ouvre le servo trois secondes.
 *
 * Bibliothèques : MFRC522, Adafruit GFX, Adafruit SSD1306, ESP32Servo.
 */
#include <SPI.h>
#include <MFRC522.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <ESP32Servo.h>

// RC522 (SPI) : SCK 18, MISO 19, MOSI 23
const uint8_t PIN_SS  = 5;
const uint8_t PIN_RST = 27;             // pas 22 : le GPIO 22 est déjà le SCL de l'écran

// OLED (I2C) : SDA 21, SCL 22
#define SCREEN_W  128
#define SCREEN_H  64
#define OLED_ADDR 0x3C

// Servo
const uint8_t  PIN_SERVO    = 13;
const int      ANGLE_CLOSED = 0;
const int      ANGLE_OPEN   = 90;
const uint32_t OPEN_MS      = 3000;

// Badges autorisés (UID de 4 octets) : ajoute une ligne par badge
const byte AUTHORIZED[][4] = {
  {0x04, 0xA3, 0x7B, 0x2C},
};
const uint8_t AUTHORIZED_COUNT = sizeof(AUTHORIZED) / sizeof(AUTHORIZED[0]);

MFRC522 rfid(PIN_SS, PIN_RST);
Adafruit_SSD1306 display(SCREEN_W, SCREEN_H, &Wire, -1);
Servo door;

bool isAuthorized() {
  if (rfid.uid.size != 4) return false;
  for (uint8_t i = 0; i < AUTHORIZED_COUNT; i++) {
    if (memcmp(rfid.uid.uidByte, AUTHORIZED[i], 4) == 0) return true;
  }
  return false;
}

String uidToString() {
  String s;
  for (byte i = 0; i < rfid.uid.size; i++) {
    if (rfid.uid.uidByte[i] < 0x10) s += '0';
    s += String(rfid.uid.uidByte[i], HEX);
    if (i < rfid.uid.size - 1) s += ' ';
  }
  s.toUpperCase();
  return s;
}

void screen(const char* title, const String& uid, const char* result) {
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.print(title);
  display.setCursor(0, 14);
  display.print(uid);
  display.setTextSize(2);
  display.setCursor(0, 40);
  display.print(result);
  display.display();
}

void setup() {
  Serial.begin(115200);
  SPI.begin(18, 19, 23, PIN_SS);
  rfid.PCD_Init();

  Wire.begin(21, 22);
  display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);

  door.setPeriodHertz(50);
  door.attach(PIN_SERVO, 500, 2400);    // impulsions min et max en microsecondes
  door.write(ANGLE_CLOSED);

  screen("BADGE", "", "");
}

void loop() {
  if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) return;

  String uid = uidToString();
  bool ok = isAuthorized();
  screen("BADGE", uid, ok ? "ACCES OK" : "REFUSE");

  if (ok) {
    door.write(ANGLE_OPEN);
    delay(OPEN_MS);
    door.write(ANGLE_CLOSED);
  } else {
    delay(1500);
  }

  screen("BADGE", "", "");
  rfid.PICC_HaltA();
  rfid.PCD_StopCrypto1();
}

Idées à construire

  • Pointeuse : enregistre chaque UID avec l’heure sur une carte microSD.
  • Boîte à souvenirs : un autocollant NFC déclenche une musique ou une lumière.
  • Compteur de passages avec un écran OLED et le nom de chaque badge.
  • Clé de démarrage pour un projet : rien ne se lance sans le bon badge.

Le PCB et les composants pour reproduire les projets arriveront avec la boutique.

Recevoir les prochaines sorties

Un email quand un projet, un guide, un PCB ou un kit est publié. Pas de spam, désinscription à tout moment.

Être prévenu

Recevez un email quand une nouveauté est publiée.