RFID

Introduction

RFID (Radio Frequency IDentification), est une technologie qui utilise des ondes radio pour identifier des objets. Le système RFID se compose principalement de trois composants :

  1. Un Tag RFID (étiquette ou jeton) qui contient une puce électronique et une antenne. La puce stocke des informations, tandis que l'antenne permet de transmettre ces informations
  2. Un lecteur RFID qui émet des ondes radio pour communiquer avec les tags RFID. Le lecteur peut être fixe ou mobile et possède également une antenne pour émettre et recevoir des signaux.
  3. Un système de traitement qui collecte, traite et analyse les informations lues par le lecteur RFID.

Principe de fonctionnement

  • Le lecteur RFID envoie des ondes radio de faible puissance à travers son antenne,
  • Lorsqu'un tag RFID se trouve dans la portée des ondes radio émises par le lecteur, son antenne capte ces ondes et active la puce électronique à l'intérieur du tag,
  • La puce du tag RFID renvoie alors une réponse au lecteur, contenant les informations stockées comme un numéro d'identification unique ou d'autres données spécifiques,
  • Le lecteur reçoit cette réponse, décode les informations et les envoie au système de traitement

Types de tags RFID

  • Tags passifs : Ils n'ont pas de source d'alimentation propre et tirent leur énergie des ondes radio émises par le lecteur. Ils ont une portée plus courte, généralement de quelques centimètres,
  • Tags actifs : Ils possèdent une batterie interne qui leur permet d'émettre des signaux de manière autonome. Ils ont une portée plus longue, pouvant aller jusqu'à plusieurs dizaines de mètres,
  • Tags semi-passifs : Ils possèdent une batterie interne, mais n'émettent pas de façon autonome. Ils utilisent la batterie pour alimenter la puce électronique lorsqu'ils sont activés par le lecteur.

Dans la suite de ce tuto, nous utiliserons un tag Mifare classic 1k

  • Tag passif
  • Fréquence: 13,56 MHz
  • Compatible avec la norme ISO/IEC 14443A
  • Mémoire de 1K contenant :
    • 16 secteurs subdivisés chacun en 4 blocs
    • Chaque bloc a une capacité de 16 octets
    • Le bloc 0 du secteur 0 contient un identifiant unique (UID) programmé en usine, qui ne peut pas être modifié
    • Le dernier bloc (Trailer block) de chaque secteur est un bloc de sécurité utilisé pour les clés d'authentification et les bits de contrôle. Nous en reparlerons plus en détail après l'exemple
    • Les autres blocs sont des blocs de données accessibles en lecture/écriture

Applications de la RFID

  • Logistique et gestion des stocks : Suivi des produits dans les entrepôts et les centres de distribution,
  • Contrôle d'accès : Utilisé dans les cartes d'accès aux bâtiments et aux installations sécurisées,
  • Identification des animaux : Suivi et identification des animaux domestiques ou d'élevage
  • Paiement sans contact : Utilisé dans les cartes de crédit et les systèmes de paiement mobile
  • Suivi des bagages : Suivi des bagages dans les aéroports pour éviter les pertes.

Exemple: Contrôle d'accès

Dans cet exemple, on va réaliser un contrôle d'accès à l'aide de la technologie RFID.
Le système comporte:

  1. Un lecteur RFID RC522
    • Alimentation 3.3V
    • Fréquence: 13,56 MHz
    • Interface SPI pour la connexion avec l'Arduino
    • Communique avec le tag selon la norme ISO/IEC 14443A
    • Distance de détection optimale: 10 mm
  2. Un Arduino UNO dont les broches de l'interface SPI sont:
    • Broche 10 : SS
    • Broche 11 : MOSI
    • Broche 12 : MISO
    • Broche 13 : SCK
  3. Un Tag RFID avec puce Mifare Classic 1K ou compatible

Schéma de branchement

Descriptif du programme

  • Le programme contient une liste d'identifiants UID autorisés
  • A chaque passage dans loop() on fait un scan pour voir s'il ya un tag à proximité
  • Si un tag est présent, on lit son identifiant unique UID qui est constitué de 4 octets
  • On arrête la communication avec le tag présent, sinon, tant qu'il est présent, il sera re-détecté et l'action sera répétée
  • On place la representation Hex des 4 octets dans une variable de type String en les séparant par un espace pour avoir le même format que les UIDs autorisés
  • On compare avec chaque élément de la liste des UIDs autorisés
  • On affiche l'UID et son autorisation

Le programme :

Commandes avec paramètres
            
/*
   ARDUINO                      RC522
   GND        ---<--------------> GND
   3.3V       ---<--------------> 3.3V
   SCK(D13)   ---<--------------> SCK
   MISO(D12)  ---<--------------> MISO
   MOSI(D11)  ---<--------------> MOSI
   SS(D10)     ---<--------------> SS
   D9         ---<--------------> RST
*/

#include "SPI.h"
#include "MFRC522.h"
#define RST_PIN  9 // RES pin
#define SS_PIN  10 // SDA (SS) pin

const char* authorizedIDs[] = {
  "6A FD 12 87",
  "79 14 06 A3",
  "79 AA 06 B3",
  "35 7C 2E 83"
};
MFRC522 mfrc522(SS_PIN, RST_PIN);

void setup() {
  Serial.begin(9600);
  SPI.begin();
  mfrc522.PCD_Init();
  delay(5);
  Serial.println("\n======================================================");
  mfrc522.PCD_DumpVersionToSerial();  // affiche Version de la bibliothèque
}

void loop() {
  // Vérifier si un tag est présent
  if (!mfrc522.PICC_IsNewCardPresent())return;
  delay(200); //le tag sera mieux positionné
  // Lire le numéro de série du tag
  if (!mfrc522.PICC_ReadCardSerial()) {
    Serial.println("Echec lecture UID");
    return;  // echec
  }
  mfrc522.PICC_HaltA(); // arreter le communication avec le tag présent
  // les 4 octets de l'UID sont dans le tableau mfrc522.uid.uidByte[]
  String UID = "";  // l'UID de type String
  bool authorized = false;
  // placer les 4 octets de l'ID dans un String
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    UID += String(mfrc522.uid.uidByte[i] < 0x10 ? "0" : ""); //ex: 0x6 --> 0x06
    UID += String(mfrc522.uid.uidByte[i], HEX);
    UID += " ";  // les octets sont séparé par un espace
  }
  UID.trim();  // supprimer le dernier espace
  UID.toUpperCase();  // mettre en majuscule

  // vérifier si l'ID détecté est autorisé
  for (int i = 0; i < sizeof(authorizedIDs) / sizeof(authorizedIDs[0]); i++) {
    if (UID.equals(authorizedIDs[i])) {
      authorized = true;
      break;
    }
  }

  // Afficher le statut d'autorisation
  if (authorized) {
    Serial.println("Tag détecté  --->" + UID + "<----    AUTORISÉ");
  } else {
    Serial.println("Tag détecté  --->" + UID + "<----    NON AUTORISÉ");
  }
}

Les blocs mémoire du tag

  • Chaque secteur est constitué de 4 blocs de 16 octets chacun. 3 blocs de données (B0, B1, B2) et un bloc de contrôle B3 (Trailer Block). C'est ce bloc qui nous intéresse ici
  • Le Trailer bloc de chaque secteur contient 2 clés d'authentification key_A et key_B et 3 bits d'accès C0, C1 et C2
  • Les clés d'authentification permettent de contrôler l'accès aux blocs du secteur
  • Les bits d'accès définissent les autorisations de chaque clé
  • Les clés Key_A et Key_B sont constituées de 6 octets chacune. Leur valeur par défaut est {0xFF 0xFF 0xFF 0xFF 0xFF 0xFF } mais on peut la modifier
  • Les 3 bits C0, C1 et C2 sont répartis dans les octets 6, 7 et 8. L'octet 9 n'a pas de fonction de contrôle
  • L'accès à chacun des 4 blocs du secteur est géré par son propre triplet {C0, C1, C2}, voici comment les bits sont réparti dans les 3 octets 6, 7 et 8.
  • On remarquera que le triplet de chaque bloc est répété 2 fois
  • Pour les blocs de donnés, la combinaison la plus permissive est {C0,C1,C2}={0,0,0}. Elle permet d'accéder aux blocs en lecture et en écriture avec la clé Key_A ou la clé Key_B
  • Pour le Trailer Bloc, la combinaison la plus permissive est {C0,C1,C2}={0,0,1}. Elle permet
    • L'accès en écriture à Key_A avec l'authentification Key_A. L'accès en lecture à Key_A n'est pas possible
    • L'accès en lecture/écriture à Key_B avec l'authentification Key_A
    • L'accès en lecture/écriture aux bits d'accès avec l'authentification Key_A
  • Avec ces deux combinaisons on obtient les octets 6, 7 et 8 suivants:
  • Les autres possibilités sont listées dans les tableau ci dessous


Exemple: Lecture d'un Bloc

L'exemple permet de lire un bloc (données ou trailer) et afficher sont contenu en Hex sur le moniteur série

Lecture d'un bloc
              
#include <SPI.h>
#include <MFRC522.h>

#define RST_PIN 9  // Pin reset
#define SS_PIN 10  // Pin Slave Select

MFRC522 mfrc522(SS_PIN, RST_PIN);  // Create MFRC522 instance
MFRC522::MIFARE_Key key = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; //Key A

void setup() {
  Serial.begin(9600);  // Initialize serial communications
  SPI.begin();         // Initialize SPI bus
  mfrc522.PCD_Init();  // Initialize MFRC522 reader
  Serial.println("\nPlacer un Tag sur le lecteur\n");
}

void loop() {
  // Voir si un Tag est à proximité
  if (!mfrc522.PICC_IsNewCardPresent()) {
    return;
  }

  // Lire l'UID du Tag
  if (!mfrc522.PICC_ReadCardSerial()) {
    Serial.println("Echec lecture UID");
    return;  // echec
  }

  byte block = 7; // le bloc à lire

  // Authentification avec la clé Key_A
  if (mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid)) != MFRC522::STATUS_OK) {
    Serial.print("Authentication failed: ");
    Serial.println(mfrc522.GetStatusCodeName(mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid))));
    return;
  }
  
  // Lire les 16 octets du bloc
  
  byte buffer[18];
  byte size = sizeof(buffer);
  MFRC522::StatusCode rstatus = mfrc522.MIFARE_Read(block, buffer, &size);
  if (rstatus != MFRC522::STATUS_OK) {
    Serial.print("Read failed: ");
    Serial.println(mfrc522.GetStatusCodeName(rstatus));
    return;
  }
  
  // afficher les octets lus
  for (byte i = 0; i < 16; i++) {
    Serial.print(buffer[i],HEX);
    Serial.print(" ");
  }
  Serial.println();

  // Halt PICC
  mfrc522.PICC_HaltA();
  // Stop encryption on PCD
  mfrc522.PCD_StopCrypto1();
}
  
  

Exemple: Écriture d'un bloc

Cet exemple écrit 16 octets dans un bloc puis les relit et les affiche pour vérifier

Écriture d'un Bloc
              
#include <SPI.h>
#include <MFRC522.h>

#define RST_PIN 9  // Pin reset
#define SS_PIN 10  // Pin Slave Select

MFRC522 mfrc522(SS_PIN, RST_PIN);  // Create MFRC522 instance
MFRC522::MIFARE_Key key = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // Key A

void setup() {
  Serial.begin(9600);  // Initialize serial communications
  SPI.begin();         // Initialize SPI bus
  mfrc522.PCD_Init();  // Initialize MFRC522 reader
  Serial.println("\n\nPlacer un tag sur le lecteur\n");
  Serial.println();
}

void loop() {
  // Voir si un Tag est à proximité
  if (!mfrc522.PICC_IsNewCardPresent()) {
    return;
  }

  // Lire l'UID du Tag
  if (!mfrc522.PICC_ReadCardSerial()) {
    Serial.println("Echec lecture UID");
    return;  // echec
  }

  byte block = 6; // Block to write to
  byte data[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};

  // Authenticate using key A
  if (mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid)) != MFRC522::STATUS_OK) {
    Serial.print("Authentication failed: ");
    Serial.println(mfrc522.GetStatusCodeName(mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid))));
    mfrc522.PICC_HaltA();
    mfrc522.PCD_StopCrypto1();
    return;
  }

  // Write data to the block
  MFRC522::StatusCode status = mfrc522.MIFARE_Write(block, data, 16);
  if (status != MFRC522::STATUS_OK) {
    Serial.print("Write failed: ");
    Serial.println(mfrc522.GetStatusCodeName(status));
    mfrc522.PICC_HaltA();
    mfrc522.PCD_StopCrypto1();
    return;
  }
  Serial.println("Données écrites : ");
  for (byte i = 0; i < 16; i++) {
    Serial.print(data[i]);
    Serial.print(" ");
  }
  Serial.println();

  // Lecture et affichage pour vérification
  byte buff[18];
  byte size = sizeof(buff);
  // Read data from the block
  MFRC522::StatusCode rstatus = mfrc522.MIFARE_Read(block, buff, &size);
  if (rstatus != MFRC522::STATUS_OK) {
    Serial.print("Read failed: ");
    Serial.println(mfrc522.GetStatusCodeName(rstatus));
    mfrc522.PICC_HaltA();
    mfrc522.PCD_StopCrypto1();
    return;
  }
  Serial.println("Données lues : ");
  for (byte i = 0; i < 16; i++) {
    Serial.print(buff[i]);
    Serial.print(" ");
  }
  Serial.println("\n=======================\n");

  // Halt PICC
  mfrc522.PICC_HaltA();
  // Stop encryption on PCD
  mfrc522.PCD_StopCrypto1();
}
  
  

Exemple: Lecture/Écriture de texte

Dans cet exemple on va écrire un prénom dans un bloc et un nom dans le bloc suivant. Ensuite on relit les deux blocs pour vérification

Pour faciliter la gestion du nombre de caractères par mot (surtout pendant la lecture), chaque mot sera complété par des espaces pour remplir les 16 octets d'un bloc. Les espaces seront facilement supprimés à la lecture à l'aide de la fonction trim()

Lecture/Écriture de texte
              
#include <SPI.h>
#include <MFRC522.h>

#define RST_PIN 9  // Pin reset
#define SS_PIN 10  // Pin Slave Select

MFRC522 mfrc522(SS_PIN, RST_PIN);  // Create MFRC522 instance
MFRC522::MIFARE_Key key = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; //Key A

void setup() {
  Serial.begin(9600);  // Initialize serial communications
  SPI.begin();         // Initialize SPI bus
  mfrc522.PCD_Init();  // Initialize MFRC522 reader
  Serial.println("\n\nPlacer un Tag sur le lecteur\n\n");
}

void loop() {
  // Voir si un Tag est à proximité
  if (!mfrc522.PICC_IsNewCardPresent()) {
    return;
  }

  // Lire l'UID du Tag
  if (!mfrc522.PICC_ReadCardSerial()) {
    Serial.println("Echec lecture UID");
    return;  // echec
  }

  // Écriture le prénom dans le bloc 4
  byte writeStatus = writeStr(4, "Abdelmajid");
  if (writeStatus != 0) {
    Serial.println("Échec d'écriture dans le bloc 4");
    mfrc522.PICC_HaltA();// Halt PICC
    mfrc522.PCD_StopCrypto1(); // Stop encryption on PCD
    return;
  }
  // Écriture du nom dans le bloc 5
  writeStatus = writeStr(5, "Oumnad");
  if (writeStatus != 0) {
    Serial.println("Échec d'écriture dans le bloc 5");
    mfrc522.PICC_HaltA();// Halt PICC
    mfrc522.PCD_StopCrypto1(); // Stop encryption on PCD
    return;
  }

  // Lecture lecture et affichage du bloc 4 et 5
  String prenom = readBlock(4);
  String nom = readBlock(5);
  Serial.print("------>");
  Serial.print(prenom + " " + nom);
  Serial.println("---<-------");
  mfrc522.PICC_HaltA(); // Halt PICC
  mfrc522.PCD_StopCrypto1(); // Stop encryption on PCD
}

// Fonction pour écrire une chaîne de caractères dans un bloc
byte writeStr(byte block, String str) {
  byte buffer[16];
  byte len = str.length();
  str.getBytes(buffer, 17);
  for (byte i = len; i < 16 ; i++) {
    buffer[i] = ' ';
  }

  // Authentification
  MFRC522::StatusCode status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid));
  if (status != MFRC522::STATUS_OK) {
    Serial.print("Authentication failed: ");
    Serial.println(mfrc522.GetStatusCodeName(status));
    return 1;  // échec de l'authentification
  }

  // Écriture des données dans le bloc
  status = mfrc522.MIFARE_Write(block, buffer, 16);
  if (status != MFRC522::STATUS_OK) {
    Serial.print("Write failed: ");
    Serial.println(mfrc522.GetStatusCodeName(status));
    return 1;  // échec de l'écriture
  }
  return 0;  // succès
}

// Fonction pour lire le contenu d'un bloc et retourner une chaîne de caractères
String readBlock(byte block) {
  byte buffer[18];
  byte size = sizeof(buffer);

  // Authentification
  MFRC522::StatusCode status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid));
  if (status != MFRC522::STATUS_OK) {
    Serial.print("Authentication failed: ");
    Serial.println(mfrc522.GetStatusCodeName(status));
    return "";
  }

  // Lecture des données du bloc
  status = mfrc522.MIFARE_Read(block, buffer, &size);
  if (status != MFRC522::STATUS_OK) {
    Serial.print("Read failed: ");
    Serial.println(mfrc522.GetStatusCodeName(status));
    return "";
  }

  String result = "";
  for (byte i = 0; i < 16; i++) {
    result += char(buffer[i]);
  }
  result.trim();
  return result;
}
  
  

Exemple: Contrôle d'accès Version 2

Dans cet exemple, on va améliorer légèrement le programme de contrôle d'accès. En plus de vérifier si l'UID du tag est autorisée, on va afficher une petite phrase sympa qui salue le nom du porteur du tag. Il faut évidement avoir déjà écrit le nom et le prénom dans les bloc 4 et 5 à l'aide du programme de l'exemple précédent


Contrôle d'accès avec salutation
            
/*
   ARDUINO                      RC522
   GND        ---<--------------> GND
   3.3V       ---<--------------> 3.3V
   SCK(D13)   ---<--------------> SCK
   MISO(D12)  ---<--------------> MISO
   MOSI(D11)  ---<--------------> MOSI
   SS(D10)    ---<--------------> SS
   D9         ---<--------------> RST
*/

#include "SPI.h"
#include "MFRC522.h"
#define RST_PIN  9 // RES pin
#define SS_PIN  10 // SDA (SS) pin

const char* authorizedIDs[] = {
  "6A FD 12 87",
  "79 14 06 A3",
  "79 AA 06 B3",
  "35 7C 2E 83"
};
MFRC522 mfrc522(SS_PIN, RST_PIN);
MFRC522::MIFARE_Key key = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};


void setup() {
  Serial.begin(9600);
  SPI.begin();
  mfrc522.PCD_Init();
  delay(5);
  Serial.println("\n======================================================");
  mfrc522.PCD_DumpVersionToSerial();  // affiche Version de la bibliothèque
  Serial.println("\nPlacer un Tag sur le lecteur\n");
}

void loop() {
  // Vérifier si un tag est présent
  if (!mfrc522.PICC_IsNewCardPresent())return;
  delay(200); //le tag sera mieux positionné
  
  // Lire le numéro de série du tag
  if (!mfrc522.PICC_ReadCardSerial()) {
    Serial.println("Echec lecture UID");
    mfrc522.PICC_HaltA(); // Halt PICC
    return;  // echec
  }
  // les 4 octets de l'UID sont dans le tableau mfrc522.uid.uidByte[]
  String UID = "";  // l'UID de type String
  bool authorized = false;
  // placer les 4 octets de l'ID dans un String
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    UID += String(mfrc522.uid.uidByte[i] < 0x10 ? "0" : ""); //ex: 0x6 --> 0x06
    UID += String(mfrc522.uid.uidByte[i], HEX);
    UID += " ";  // les octets sont séparé par un espace
  }
  UID.trim();  // supprimer le dernier espace
  UID.toUpperCase();  // mettre en majuscule

  // vérifier si l'ID détecté est autorisé
  for (int i = 0; i < sizeof(authorizedIDs) / sizeof(authorizedIDs[0]); i++) {
    if (UID.equals(authorizedIDs[i])) {
      authorized = true;
      break;
    }
  }

   // Lecture du nom et du prénom dans bloc 4 et 5
  String prenom = readBlock(4);
  String nom = readBlock(5);
  Serial.print("Bonjour  ");
  Serial.print(prenom + " " + nom);

  // Afficher le statut d'autorisation
  if (authorized) {
    Serial.println(" ,         Accès autorisé");
  } else {
    Serial.println(" ,         Accès refusé");
  }
  mfrc522.PICC_HaltA(); // Halt PICC
  mfrc522.PCD_StopCrypto1(); // Stop encryption on PCD
}

// Fonction pour lire le contenu d'un bloc et retourner une chaîne de caractères
String readBlock(byte block) {
  byte buffer[18];
  byte size = sizeof(buffer);

  // Authentification
  MFRC522::StatusCode status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid));
  if (status != MFRC522::STATUS_OK) {
    Serial.print("Authentication failed: ");
    Serial.println(mfrc522.GetStatusCodeName(status));
    return "";
  }

  // Lecture des données du bloc
  status = mfrc522.MIFARE_Read(block, buffer, &size);
  if (status != MFRC522::STATUS_OK) {
    Serial.print("Read failed: ");
    Serial.println(mfrc522.GetStatusCodeName(status));
    return "";
  }

  String result = "";
  for (byte i = 0; i < 16; i++) {
    result += char(buffer[i]);
  }
  result.trim();
  return result;
}