Système de contrôle d’accès distribué avec nœuds Arduino PoE, synchronisation NTP et réplication sécurisée des badges via RS485 multi-maître.
// Système de contrôle d'accès distribué avec Arduino Mega, PoE, synchronisation NTP et réplication de la base de données des badges via RS485
#include <SPI.h>
#include <Ethernet.h>
#include <NTPClient.h>
#include <EthernetUdp.h>
#include <MFRC522.h>
#include <RTClib.h>
#include <ModbusMaster.h>
// --- CONFIGURATION DES BROCHES ---
// Ethernet W5500 SPI
#define ETH_CS_PIN 10
#define ETH_INT_PIN 2
// MFRC522 RFID
#define RFID_SS_PIN 53
#define RFID_RST_PIN 9
// RS485 MAX485
#define RS485_DE_RE_PIN 8
// I2C pour RTC
// SDA : broche 20, SCL : broche 21 (I2C matériel)
// --- CONFIGURATION RÉSEAU ---
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192, 168, 1, 100); // IP statique, modifier pour chaque nœud
unsigned int localPort = 8888; // port UDP pour NTP
// --- VARIABLES GLOBALES ---
EthernetUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 0, 60000); // mise à jour toutes les 60s
MFRC522 mfrc522(RFID_SS_PIN, RFID_RST_PIN);
RTC_DS3231 rtc;
ModbusMaster node;
// Tampon pour base de données des badges (exemple simple, max 100 badges)
#define MAX_BADGES 100
#define BADGE_ID_LEN 4 // UID de 4 octets
byte badgeDatabase[MAX_BADGES][BADGE_ID_LEN];
uint8_t badgeCount = 0;
// ID nœud RS485 (à configurer de manière unique pour chaque nœud)
#define NODE_ID 1
// Fonction pour activer la transmission RS485
void rs485TransmitEnable(bool enable) {
digitalWrite(RS485_DE_RE_PIN, enable ? HIGH : LOW);
}
// Fonction pour comparer deux UID de badge
bool confrontaUID(byte *uid1, byte *uid2) {
for (int i = 0; i < BADGE_ID_LEN; i++) {
if (uid1[i] != uid2[i]) return false;
}
return true;
}
// Ajoute un badge à la base de données s'il n'est pas présent
bool aggiungiBadge(byte *uid) {
for (int i = 0; i < badgeCount; i++) {
if (confrontaUID(badgeDatabase[i], uid)) return false; // déjà présent
}
if (badgeCount < MAX_BADGES) {
memcpy(badgeDatabase[badgeCount], uid, BADGE_ID_LEN);
badgeCount++;
return true;
}
return false; // base de données pleine
}
// Vérifie si le badge est autorisé
bool badgeAutorizzato(byte *uid) {
for (int i = 0; i < badgeCount; i++) {
if (confrontaUID(badgeDatabase[i], uid)) return true;
}
return false;
}
// Fonction pour envoyer la base de données des badges via RS485 (réplication)
void inviaDatabase() {
// Protocole simple : envoi d'un paquet avec en-tête, nombre de badges, liste des UID
rs485TransmitEnable(true);
delay(10); // stabiliser la ligne
Serial1.write(0xAA); // en-tête début
Serial1.write(NODE_ID); // ID nœud émetteur
Serial1.write(badgeCount); // nombre de badges
for (int i = 0; i < badgeCount; i++) {
Serial1.write(badgeDatabase[i], BADGE_ID_LEN);
}
Serial1.write(0x55); // en-tête fin
Serial1.flush();
delay(10);
rs485TransmitEnable(false);
}
// Fonction pour recevoir et traiter la base de données des autres nœuds
void riceviDatabase() {
static enum {WAIT_START, READ_ID, READ_COUNT, READ_UIDS, WAIT_END} state = WAIT_START;
static byte buffer[MAX_BADGES * BADGE_ID_LEN];
static uint8_t count = 0;
static uint8_t index = 0;
static uint8_t senderID = 0;
while (Serial1.available()) {
byte c = Serial1.read();
switch(state) {
case WAIT_START:
if (c == 0xAA) {
state = READ_ID;
}
break;
case READ_ID:
senderID = c;
state = READ_COUNT;
break;
case READ_COUNT:
count = c;
if (count > MAX_BADGES) {
// erreur, réinitialiser
state = WAIT_START;
} else {
index = 0;
state = READ_UIDS;
}
break;
case READ_UIDS:
buffer[index++] = c;
if (index >= count * BADGE_ID_LEN) {
state = WAIT_END;
}
break;
case WAIT_END:
if (c == 0x55) {
// Traiter la base de données reçue
for (int i = 0; i < count; i++) {
byte *uid = &buffer[i * BADGE_ID_LEN];
aggiungiBadge(uid); // ajoute seulement si nouveau
}
}
state = WAIT_START;
break;
}
}
}
// Fonction pour enregistrer un événement d'accès
void logEvento(const char* evento, byte *uid) {
Serial.print("[");
Serial.print(rtc.now().timestamp());
Serial.print("] ");
Serial.print(evento);
Serial.print(" UID: ");
for (int i = 0; i < BADGE_ID_LEN; i++) {
if (uid[i] < 0x10) Serial.print("0");
Serial.print(uid[i], HEX);
Serial.print(" ");
}
Serial.println();
}
void setup() {
Serial.begin(115200); // debug
Serial1.begin(9600); // RS485
pinMode(RS485_DE_RE_PIN, OUTPUT);
rs485TransmitEnable(false); // réception RS485
// Initialiser Ethernet PoE
SPI.begin();
Ethernet.begin(mac, ip);
// Initialiser NTP
timeClient.begin();
// Initialiser RTC
if (!rtc.begin()) {
Serial.println("Erreur RTC");
while (1);
}
if (rtc.lostPower()) {
Serial.println("RTC a perdu l'alimentation, réglage de l'heure via NTP");
timeClient.update();
DateTime now = DateTime(timeClient.getEpochTime());
rtc.adjust(now);
}
// Initialiser MFRC522
mfrc522.PCD_Init();
// Initialiser ModbusMaster (non utilisé dans cet exemple mais prêt)
node.begin(NODE_ID, Serial1);
Serial.println("Système de contrôle d'accès démarré");
}
void loop() {
// Synchroniser l'heure NTP périodiquement
if (timeClient.update()) {
DateTime now = DateTime(timeClient.getEpochTime());
rtc.adjust(now);
}
// Gestion de la communication RS485 (réception base de données)
riceviDatabase();
// Lecture du badge RFID
if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
byte uid[BADGE_ID_LEN] = {0};
// Copier UID (4 octets)
for (byte i = 0; i < BADGE_ID_LEN; i++) {
uid[i] = mfrc522.uid.uidByte[i];
}
if (badgeAutorizzato(uid)) {
logEvento("Accès autorisé", uid);
// Actions d'accès (ex. ouvrir porte) ici
} else {
logEvento("Accès refusé", uid);
}
mfrc522.PICC_HaltA();
}
// Réplication périodique de la base de données (ex. toutes les 5 minutes)
static unsigned long lastReplica = 0;
if (millis() - lastReplica > 300000) {
inviaDatabase();
lastReplica = millis();
}
// Watchdog logiciel (optionnel)
// Implémenter un reset si nécessaire
}Arduino Mega 2560 (pour plus de broches et de RAM)
Module Ethernet PoE W5500 (alimentation et communication réseau)
Module RTC DS3231 (sauvegarde horaire locale en cas de perte NTP)
Module RFID MFRC522 (lecture des badges RFID 13,56 MHz)
Transceiver RS485 MAX485 (pour communication série différentielle multi-nœuds)
Alimentation PoE conforme IEEE 802.3af/at
Câbles Ethernet CAT5e/CAT6 avec connecteurs RJ45
Câbles paires torsadées pour RS485 (de préférence blindés)
Bibliothèque Arduino Ethernet (Ethernet.h)
Bibliothèque NTPClient (pour synchronisation horaire via UDP)
Bibliothèque MFRC522 (gestion du lecteur RFID)
Bibliothèque RTClib (gestion du RTC DS3231)
Bibliothèque ModbusMaster (pour protocole RS485 multi-maître)
Configuration du câblage :