I2L 2025 Groupe8

De wiki-se.plil.fr
Aller à la navigation Aller à la recherche

Proposition de système (étudiants)

Notre projet consiste à mettre en place un système d’authentification pour une application utilisant un badge électronique NFC. L’objectif est de permettre à un utilisateur de se connecter rapidement et de manière sécurisée sans saisir de mot de passe. L’utilisateur présente son badge au lecteur, si l’identification est valide, l’application se débloque et une LED verte ou un message indique le succès, si l’identification est invalide, l’accès est refusé et une LED rouge ou un message indique l’échec. Chaque tentative peut être enregistrée pour un suivi des accès.

Contre-proposition (intervenant)

Proposez un mode autonome et un mode connecté via USB. Le lecteur NFC PN532 semble pouvoir fonctionner en 3.3V (avec la batterie) ou en 5V (connexion USB).

En mode autonome, vous pouvez par exemple afficher des informations sur la carte ou le badge identifié. En mode connecté vous pouvez effectivement débloquer une application. L'application communiquera avec la carte via la classe USB "vendeur" (spécifique). Il suffit d'une interruption IN pour savoir si une carte a été identifiée.

Une bibliothèque C pour gérer un lecteur PN532 semble disponible [1].

Une bibliothèque pour gérer le contrôleur graphique SDD1306 est aussi disponible [2].

Proposition définitive (étudiants)

Mise en place d’un système d’authentification pour une application reposant sur l’utilisation d’un badge électronique NFC.

Après le scan du badge, l’utilisateur accède à un menu permettant :

  • d’authentifier et de déverrouiller l’application,
  • d’écrire ou de mettre à jour les informations stockées sur le badge.
  • de lire les données présentes sur le badge (nom, prénom, mot de passe, etc.),

L’application propose deux modes de fonctionnement :

  • un mode autonome,
  • un mode connecté via USB.

Une interface dédiée est également disponible pour les besoins de test et de validation du système.

Répartition du travail (étudiants)

Concernant la répartition des tâches, l’un a travaillé sur le mode autonome tandis que l’autre s’est concentré sur le mode connecté via USB. Toutefois, nous avons constamment collaboré et nous nous sommes mutuellement aidés tout au long du projet.

Carte

Schéma initial (étudiants)

Schéma de la carte

Carte routée (intervenant)

Vous utiliserez la carte avec l'écran OLED. Vous avez deux exemplaires de la carte suivant que vous souhaitez utiliser une connexion I2C ou série avec le lecteur NFC.

Composants (intervenant)

Carte réalisée (intervenant)

Photo de la carte

La carte est entiérement soudée. Eventuellement vous pouvez demander l'ajout d'un buzzer.

Travaux (étudiants)

Carte au 13/10/2025

Voici ce qu'on a pu tester :

- Clignotement des LEDs avec des boutons respectifs

- Mise en place de l'horloge (1000ms)

Carte au 16/10/2025

Voici ce qu'on a pu tester :

- Test de l'écran OLED : problème avec l'écran

- Test du NFC I2C en cours

- Test du NFC SPI en cours

Carte au 13/11/2025

- Écran OLED

On a créé une librairie SSD1306 (inspirée d'une déjà existante) pour faire fonctionner notre écran avec le microcontrôleur.

- Mode autonome

On a réussi à communiquer le microcontrôleur avec le NFC via I2C, pour cela, on a créé une librairie PN532 (inspirée d'une déjà existante).

- Mode connecté USB

On a réussi à transformer le microcontrôleur en périphérique USB en utilisant LUFA. Alors, on a pu échanger des données de notre microcontrôleur vers le PC et a pu débloqué le site facilement.

On a réussi à écrire sur la carte (badge) pour stocker des informations.

- Remarque :

Pour le badge (bleu), on a constaté qu'il faudrait avoir une certaine distance pour que ça fonctionne, et le blanc (petit rectangle) il faut coller directement sur le lecteur NFC.

Rendu Final

Description du Projet

L'objectif de ce projet est de réaliser un système d'authentification physique pour une application Web via la technologie NFC. Le système repose sur une architecture matérielle composée d'un microcontrôleur ATmega32u4 et d'un contrôleur NFC PN532. La partie logicielle comprend une application Web de démonstration et une API Python (faisant office de passerelle) pour assurer la communication série (USB) entre le matériel et l'interface Web.

Le système propose deux modes de fonctionnement :

  1. Mode autonome : Ce mode permet d'utiliser le lecteur NFC de manière indépendante. Grâce à un menu interactif sur l'écran OLED, l'utilisateur peut lire et visualiser les informations des badges (UID, nom, prénom) directement sur le boîtier, sans nécessiter de connexion à un ordinateur.
  2. Mode connecté (USB) : Une fois le module connecté à l'ordinateur, ce mode active la communication avec l'API Python. Il permet trois actions principales :
    • Lecture étendue : Transmission des données du badge scanné vers l'ordinateur.
    • Authentification Web : Déverrouillage de l'application Web par simple scan d'un badge enregistré.
    • Écriture : Enregistrement de nouveaux utilisateurs en écrivant les données (nom, prénom, mot de passe) sur une carte vierge depuis l'interface.

Matériel utilisé :

Carte complete intégrant le lecteur NFC
Badge NFC
Badge NFC 2.png

PS: Pour le badge (bleu), on a constaté qu'il faudrait avoir une certaine distance pour que ça fonctionne, et le blanc (petit rectangle) il faut coller directement sur le lecteur NFC.

La réalisation de ce projet nécessitait l'interaction avec deux périphériques principaux : le lecteur NFC et l'écran OLED SSD1306. La plupart des solutions open-source existantes (comme ssd1306 ou pn532-lib) étant conçues spécifiquement pour l'environnement Arduino, elles n'étaient pas directement compatibles avec notre architecture logicielle sur l'ATmega32u4.

Nous avons donc développé nos propres bibliothèques en nous inspirant de l’existant :

  1. Communication I2C : Nous avons d'abord écrit une bibliothèque bas niveau pour gérer le protocole I2C sur le microcontrôleur. C'est la fondation utilisée par nos autres pilotes.
  2. Gestion de l'écran OLED : En nous basant sur la bibliothèque de lexus2k, nous avons réécrit un pilote optimisé et compatible avec notre couche I2C.
  3. Gestion du lecteur NFC : De même, pour le PN532, nous avons adapté les bibliothèques Arduino existantes pour créer une version autonome utilisant notre pilote I2C.

Extraits significatifs de code (étudiants)

1. Badge NFC

Les données d’un badge NFC sont stockées dans des blocs mémoire bien définis, chacun étant identifié par une adresse numérique.

Le badge est donc découpé en plusieurs blocs, et chaque bloc peut contenir une information spécifique.

Dans notre cas, nous avons choisi d’organiser les données de la manière suivante : le nom est stocké dans le bloc 1, le prénom dans le bloc 2, et le mot de passe dans le bloc 5, comme indiqué ci-dessous.

// Configuration Blocs NFC
#define BLOCK_ADDR_NAME    1
#define BLOCK_ADDR_PRENOM  2
#define BLOCK_ADDR_PASS    5

Cette fonction ci dessous écrit des données dans un bloc spécifique d’un badge NFC après avoir authentifié l’accès. Elle complète le bloc à 16 octets avec des zéros si nécessaire et retourne true si l’écriture réussit, sinon false.

bool helper_write_block(PN532 *pn532, uint8_t block_addr, uint8_t *uid, int32_t uid_len, 
                               uint8_t *data, const char *label) {
    // Préparation du bloc de 16 octets 
    // Remplissage 0 si data plus court
    uint8_t block_data[BLOCK_SIZE];
    memset(block_data, 0, BLOCK_SIZE);
    
    size_t len = strlen((char*)data);
    if (len > BLOCK_SIZE) len = BLOCK_SIZE;
    memcpy(block_data, data, len);

    if (PN532_MifareClassicAuthenticateBlock(pn532, uid, uid_len, block_addr, MIFARE_CMD_AUTH_A, KEY_DEFAULT) != PN532_ERROR_NONE)
        return false;

    return (PN532_MifareClassicWriteBlock(pn532, block_data, block_addr) == PN532_ERROR_NONE);
}

Voici la méthode permettant de lire un bloc et d'afficher son contenu à l'écran :

// Lit un bloc. Si dest_buffer != NULL, copie les données brutes.
bool helper_read_block(PN532 *pn532, uint8_t block_addr, uint8_t *uid, int32_t uid_len, 
                              uint8_t *dest_buffer, const char *label, uint8_t ui_line) {
    uint8_t raw_data[BLOCK_SIZE];
    char display_str[32];
    char safe_content[BLOCK_SIZE + 1];

    // Authentification + Lecture
    if (PN532_MifareClassicAuthenticateBlock(pn532, uid, uid_len, block_addr, MIFARE_CMD_AUTH_A, KEY_DEFAULT) == PN532_ERROR_NONE &&
        PN532_MifareClassicReadBlock(pn532, raw_data, block_addr) == PN532_ERROR_NONE) 
    {
        // Copie vers destination
        if (dest_buffer != NULL)
            memcpy(dest_buffer, raw_data, BLOCK_SIZE);

        // Nettoyage pour affichage
        memcpy(safe_content, raw_data, BLOCK_SIZE);
        safe_content[BLOCK_SIZE] = '\0';
        for (int i = 0; i < BLOCK_SIZE; i++)
            if (!isprint((unsigned char)safe_content[i])) safe_content[i] = ' ';

        snprintf(display_str, sizeof(display_str), "%s: %s", label, safe_content);
        ssd1306_print_utf8_center(display_str, ui_line);
        return true;
    } 
    else {
        snprintf(display_str, sizeof(display_str), "%s: Erreur", label);
        ssd1306_print_utf8_center(display_str, ui_line);
        return false;
    }
}


1. Mode autonome

Pour le mode autonome sans PC, on a créée cette fonction ci-dessous. Elle attend qu’une carte NFC soit présentée, lit ses données principales (nom, prénom), les affiche sur l’écran OLED, les logge sur USB, puis remet le système dans son état initial.

void action_infos(void) {
    uint8_t uid[MIFARE_UID_MAX_LENGTH];
    
    ssd1306_clear();
    ui_wait_card_screen("pour infos...");
    PORTD |= (1 << LED6);

    int32_t uid_len = detect_card(&pn532, uid);
    if (uid_len <= 0) {
        ui_show_msg("Pas de carte", 3);
        _delay_ms(DELAY_MSG_SHORT);
        goto cleanup;
    }

    ssd1306_clear();

    ui_format_and_print_uid(uid, uid_len);

    helper_read_block(&pn532,BLOCK_ADDR_NAME,   uid, uid_len, NULL, "Nom",    3);
    helper_read_block(&pn532,BLOCK_ADDR_PRENOM, uid, uid_len, NULL, "Prenom", 5);

    _delay_ms(SELECTION_DELAY);

cleanup:
    PORTD &= ~(1 << LED6);
    ssd1306_clear();
}

2. Mode connecté USB

a) Code LUFA

Voici les codes permettant à notre microcontrôleur de se comporter comme un périphérique USB lorsqu’il est connecté à un ordinateur.

On propose aussi un mode connecté USB sur PC pour pouvoir gérer les informations présentes sur le badge :


4. Librairies

a) Librairie SSD1306

Après avoir testé plusieurs codes et bibliothèques existantes (dont le fonctionnement n’était pas satisfaisant), nous avons finalement choisi de créer et d’adapter notre propre version à partir des différentes implémentations étudiées.

Cette nouvelle version de la librairie SSD1306 permet :

  • un meilleur fonctionnement de l’écran OLED,
  • une affichage plus lisible et plus fluide,
  • une meilleure maîtrise des fonctions utilisées, adaptées à notre matériel et à nos besoins.

Ci-dessous figurent les en-têtes des méthodes utilisées dans cette librairie améliorée :

#ifndef SSD1306_AVR_SSD1306_H
#define SSD1306_AVR_SSD1306_H

#pragma once
#include <stdint.h>
#include <stddef.h>

#define SSD1306_I2C_ADDR 0x3C
#define SSD1306_CTRL_CMD 0x00
#define SSD1306_CTRL_DATA 0x40

#define SSD1306_WIDTH 128
#define SSD1306_HEIGHT 64
#define SSD1306_PAGES (SSD1306_HEIGHT/8)

void ssd1306_init(void);
void ssd1306_send_command(uint8_t cmd);
void ssd1306_send_data_byte(uint8_t data);
void ssd1306_send_data(const uint8_t *buf, size_t len);
void ssd1306_set_cursor(uint8_t page, uint8_t col); // page: 0..7, col: 0..127
void ssd1306_clear(void);
void ssd1306_clear_page(uint8_t page);

#endif //SSD1306_AVR_SSD1306_H

Voici le code permettant de bien positionner le bloc de texte :

#ifndef SSD1306_AVR_SSD1306_TEXT_H
#define SSD1306_AVR_SSD1306_TEXT_H
#pragma once
#include <stdint.h>

// Print UTF-8 string with optional max width and optional scroll offset
void ssd1306_print_utf8_with_max_width_scroll(const char *s, uint8_t start_page, uint8_t start_col,
                                              uint8_t max_width_px, uint16_t scroll_px);

// Print UTF-8 string across the full width
void ssd1306_print_utf8(const char *s, uint8_t start_page, uint8_t start_col);

// Print UTF-8 string with optional max width
void ssd1306_print_utf8_with_max_width(const char *s, uint8_t start_page, uint8_t start_col, uint8_t max_width_px);

// Print UTF-8 string centered on the page
void ssd1306_print_utf8_center(const char *s, uint8_t page) ;
// Print a single character at a specific page/column
void ssd1306_putc(char c, uint8_t page, uint8_t col);

void ssd1306_print_utf8_scroll(const char *s, uint8_t page, uint8_t start_col,
                               uint8_t max_width_px, uint16_t scroll_offset);
uint16_t measure_string_px(const char *s);

#endif //SSD1306_AVR_SSD1306_TEXT_H

Voici le code permettant de formater les caractères affichés à l'écran :

#ifndef SSD1306_AVR_FONT5X7_H
#define SSD1306_AVR_FONT5X7_H

#pragma once
#include <stdint.h>

extern const uint8_t font5x7[][5];
extern const uint8_t font_accent[][5];

// Optional helpers to know counts (adjust if needed)
#define FONT5X7_FIRST 32
#define FONT5X7_LAST  126
#define FONT5X7_COLS  5

#endif //SSD1306_AVR_FONT5X7_H


Lien Github du librairie : https://github.com/laurrnci22/AccessBadge/tree/main/lufa-LUFA-210130-NSI/I2L/Minimal/libs/libssd1306-atmega328p


b) Librairie PN532

Cette nouvelle librairie PN532 permet d’assurer un fonctionnement fiable du module NFC en utilisant le protocole de communication I2C, et de lire correctement les données contenues dans le badge.

Les principaux objectifs de cette adaptation étaient :

  • garantir une communication I2C stable avec le PN532,
  • assurer une lecture fiable des éléments du badge,
  • simplifier l’intégration avec le microcontrôleur,
  • mieux maîtriser les échanges bas niveau avec le module NFC.
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>

#ifndef PN532_LIB_ATMEGA32U4_PN532_H
#define PN532_LIB_ATMEGA32U4_PN532_H


#define MIFARE_UID_MAX_LENGTH               MIFARE_UID_TRIPLE_LENGTH
#define MIFARE_UID_TRIPLE_LENGTH            (10)
#define MIFARE_CMD_AUTH_A                   (0x60)
#define MIFARE_CMD_AUTH_B                   (0x61)
#define MIFARE_CMD_READ                     (0x30)
#define MIFARE_CMD_WRITE                    (0xA0)
#define MIFARE_CMD_TRANSFER                 (0xB0)
#define MIFARE_CMD_DECREMENT                (0xC0)
#define MIFARE_CMD_INCREMENT                (0xC1)
#define MIFARE_CMD_STORE                    (0xC2)
#define MIFARE_ULTRALIGHT_CMD_WRITE         (0xA2)
#define MIFARE_UID_SINGLE_LENGTH            (4)
#define MIFARE_UID_DOUBLE_LENGTH            (7)
#define MIFARE_KEY_LENGTH                   (6)
#define MIFARE_BLOCK_LENGTH                 (16)


#define PN532_COMMAND_READGPIO              (0x0C)
#define PN532_COMMAND_WRITEGPIO             (0x0E)
#define PN532_COMMAND_INDATAEXCHANGE        (0x40)
#define PN532_COMMAND_DIAGNOSE              (0x00)
#define PN532_COMMAND_GETFIRMWAREVERSION    (0x02)
#define PN532_COMMAND_GETGENERALSTATUS      (0x04)
#define PN532_COMMAND_READREGISTER          (0x06)
#define PN532_COMMAND_WRITEREGISTER         (0x08)
#define PN532_COMMAND_SETSERIALBAUDRATE     (0x10)
#define PN532_COMMAND_SETPARAMETERS         (0x12)
#define PN532_COMMAND_SAMCONFIGURATION      (0x14)
#define PN532_COMMAND_POWERDOWN             (0x16)
#define PN532_COMMAND_RFCONFIGURATION       (0x32)
#define PN532_COMMAND_RFREGULATIONTEST      (0x58)
#define PN532_COMMAND_INJUMPFORDEP          (0x56)
#define PN532_COMMAND_INJUMPFORPSL          (0x46)
#define PN532_COMMAND_INLISTPASSIVETARGET   (0x4A)
#define PN532_COMMAND_INATR                 (0x50)
#define PN532_COMMAND_INPSL                 (0x4E)
#define PN532_COMMAND_INCOMMUNICATETHRU     (0x42)
#define PN532_COMMAND_INDESELECT            (0x44)
#define PN532_COMMAND_INRELEASE             (0x52)
#define PN532_COMMAND_INSELECT              (0x54)
#define PN532_COMMAND_INAUTOPOLL            (0x60)
#define PN532_COMMAND_TGINITASTARGET        (0x8C)
#define PN532_COMMAND_TGSETGENERALBYTES     (0x92)
#define PN532_COMMAND_TGGETDATA             (0x86)
#define PN532_COMMAND_TGSETDATA             (0x8E)
#define PN532_COMMAND_TGSETMETADATA         (0x94)
#define PN532_COMMAND_TGGETINITIATORCOMMAND (0x88)
#define PN532_COMMAND_TGRESPONSETOINITIATOR (0x90)
#define PN532_COMMAND_TGGETTARGETSTATUS     (0x8A)
#define PN532_PREAMBLE                      (0x00)
#define PN532_STARTCODE1                    (0x00)
#define PN532_STARTCODE2                    (0xFF)
#define PN532_POSTAMBLE                     (0x00)
#define PN532_HOSTTOPN532                   (0xD4)
#define PN532_PN532TOHOST                   (0xD5)
#define PN532_ERROR_NONE                                                (0x00)


#define NTAG2XX_BLOCK_LENGTH                (4)


/* Define */
#define PN532_MIFARE_ISO14443A              (0x00)
#define PN532_STATUS_ERROR                                              (-1)
#define PN532_STATUS_OK                                                 (0)


typedef struct _PN532 {
    int (*reset)(void);
    int (*read_data)(uint8_t* data, uint16_t count);
    int (*write_data)(uint8_t *data, uint16_t count);
    bool (*wait_ready)(uint32_t timeout);
    int (*wakeup)(void);
    void (*log)(const char* log);
} PN532;

int PN532_WriteFrame(PN532* pn532, uint8_t* data, uint16_t length);
int PN532_ReadFrame(PN532* pn532, uint8_t* buff, uint16_t length);
int PN532_CallFunction(PN532* pn532, uint8_t command, uint8_t* response, uint16_t response_length, uint8_t* params, uint16_t params_length, uint32_t timeout);
int PN532_GetFirmwareVersion(PN532* pn532, uint8_t* version);
int PN532_SamConfiguration(PN532* pn532);
int PN532_ReadPassiveTarget(PN532* pn532, uint8_t* response, uint8_t card_baud, uint32_t timeout);
int PN532_MifareClassicAuthenticateBlock(PN532* pn532, uint8_t* uid, uint8_t uid_length, uint16_t block_number, uint16_t key_number, uint8_t* key);
int PN532_MifareClassicReadBlock(PN532* pn532, uint8_t* response, uint16_t block_number);
int PN532_MifareClassicWriteBlock(PN532* pn532, uint8_t* data, uint16_t block_number);
int PN532_Ntag2xxReadBlock(PN532* pn532, uint8_t* response, uint16_t block_number);
int PN532_Ntag2xxWriteBlock(PN532* pn532, uint8_t* data, uint16_t block_number);
int PN532_ReadGpio(PN532* pn532, uint8_t* pins_state);
bool PN532_ReadGpioP(PN532* pn532, uint8_t pin_number);
bool PN532_ReadGpioI(PN532* pn532, uint8_t pin_number);
int PN532_WriteGpio(PN532* pn532, uint8_t* pins_state);
int PN532_WriteGpioP(PN532* pn532, uint8_t pin_number, bool pin_state);


#endif //PN532_LIB_ATMEGA32U4_PN532_H


Lien Github du librairie : https://github.com/laurrnci22/AccessBadge/tree/main/lufa-LUFA-210130-NSI/I2L/Minimal/libs/libpn532-atmega328p

Démonstrations


Affichage des informations sur le badge et authentification


Écriture sur le badge et affichage des infos

Rendus (étudiants)

Projet KiCAD : Fichier:I2L-2025-Carte-G8-final.zip

Programmes :