Documentation
SmartLib
Guide technique complet du système de gestion d'emprunts de livres SmartLib — Raspberry Pi 4, RFID-RC522, Python/Tkinter, SQLite, et services réseau.
SmartLib est une solution autonome et économique pour remplacer la gestion manuelle des bibliothèques scolaires. Le système tourne entièrement sur un Raspberry Pi 4, sans serveur externe requis.
| Composant | Technologie | Rôle |
|---|---|---|
| Authentification | RFID-RC522 + MIFARE 1k | Identifier les élèves par badge |
| Interface | Python 3 + Tkinter | GUI client et administration |
| Stockage | SQLite (smartlib.db) | Livres, emprunts, utilisateurs |
| Documents | ReportLab (PDF) + SMTP | Reçus automatiques par e-mail |
| Réseau local | Samba + mDNS (Avahi) | Partage des reçus PDF |
| Cloud | AlwaysData + Paramiko (SFTP) | Backup distant de la base |
| Logs | Logging + SysLogHandler | Traçabilité de tous les événements |
| Planification | Cron | Backup et nettoyage automatiques |
Prérequis
Avant de déployer SmartLib, assurez-vous de disposer des éléments suivants.
Installation des dépendances Python
# Mise à jour du système sudo apt update && sudo apt upgrade -y # Dépendances système sudo apt install -y python3 python3-pip python3-tk samba avahi-daemon # Modules Python pip install reportlab paramiko mfrc522 RPi.GPIO --break-system-packages # Activer SPI sudo raspi-config # → Interface Options → SPI → Enable
Câblage RFID-RC522
Le module RFID-RC522 se connecte au Raspberry Pi 4 via 7 câbles GPIO (Dupont mâle-femelle) en utilisant l'interface SPI.
| Pin RC522 | Pin GPIO Raspberry Pi | Numéro physique | Description |
|---|---|---|---|
| SDA | GPIO 8 (CE0) | 24 | Chip Select |
| SCK | GPIO 11 (SCLK) | 23 | Clock SPI |
| MOSI | GPIO 10 (MOSI) | 19 | Master Out Slave In |
| MISO | GPIO 9 (MISO) | 21 | Master In Slave Out |
| IRQ | — | — | Non utilisé |
| GND | GND | 6 | Masse |
| RST | GPIO 25 | 22 | Reset |
| 3.3V | 3.3V | 1 | Alimentation |
Configuration SPI
Le protocole SPI (Serial Peripheral Interface) est utilisé pour la communication entre le Raspberry Pi et le RFID-RC522. Il doit être activé manuellement.
Vérification du module SPI
# Vérifier que SPI est activé lsmod | grep spi # Doit afficher : spidev spi_bcm2835 # Test rapide de lecture RFID python3 -c "from mfrc522 import SimpleMFRC522; r=SimpleMFRC522(); print(r.read())"
Schéma de la base de données
Le fichier smartlib.db contient 4 tables. Il est stocké à /home/abdel/tfe/smartlib.db.
Initialisation de la base de données
# Configuration USER_HOME = "/home/abdel" DB_PATH = "/home/abdel/tfe/smartlib.db" #Base de donnée def init_db(): conn = sqlite3.connect(DB_PATH) c = conn.cursor() c.execute("""CREATE TABLE IF NOT EXISTS books ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, author TEXT, genre TEXT DEFAULT 'Général', quantity INTEGER DEFAULT 1 )""") c.execute("""CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT UNIQUE NOT NULL, name TEXT NOT NULL, email TEXT DEFAULT '' )""") c.execute("""CREATE TABLE IF NOT EXISTS loans ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER REFERENCES users(id), book_id INTEGER REFERENCES books(id), borrow_date DATETIME, due_date DATETIME, return_date DATETIME, status TEXT DEFAULT 'En cours' )""") conn.commit() conn.close()
Module RFID
Deux fonctions principales gèrent le RFID : read_rfid() pour lire un badge et register_student_rfid() pour enregistrer un nouvel élève.
#RFID def read_rfid(): try: from mfrc522 import SimpleMFRC522 r = SimpleMFRC522() uid, _ = r.read() logger.info(f"🔖 RFID détecté - UID: {uid}") return str(uid) except Exception as e: logger.error(f"✗ Erreur lecture RFID: {e}") return None finally: GPIO.cleanup()
SimpleMFRC522 est issue du projet miguelbalboa/rfid. Le GPIO.cleanup() en finally libère les broches même en cas d'erreur.Enregistrement d'un badge
Ouvrir la fenêtre d'enregistrement
Cliquer sur le bouton □ Enregistrer dans l'interface admin. Une fenêtre Tkinter s'ouvre avec un champ pour le nom de l'élève.
Approcher le badge
Appuyer sur □ Lire & Enregistrer. La fonction read_rfid() est appelée — elle attend un badge sur le lecteur.
Enregistrement en base
L'UID lu est inséré dans la table users avec le nom de l'élève via INSERT INTO users VALUES (?,?,?). Un log est créé.
Module E-mail (SMTPLib)
Chaque emprunt déclenche un envoi automatique d'e-mail avec le reçu PDF en pièce jointe. L'envoi est asynchrone via threading pour ne pas bloquer l'interface.
# Configuration SMTP SMTP_EMAIL = "abdelwadoud.bako@gmail.com" SMTP_PASSWORD = "uhcamcmeuuonrlvt" # App password Gmail DESTINATAIRE = "abdelwadoud.bako@inraci.be" def send_email_with_pdf(pdf_path, book_title): if not SMTP_EMAIL or not SMTP_PASSWORD: return # Attendre que le PDF soit généré (max 2,5s) wait = 0 while not os.path.exists(pdf_path) and wait < 5: time.sleep(0.5); wait += 0.5 try: msg = MIMEMultipart() msg['From'], msg['To'] = SMTP_EMAIL, DESTINATAIRE msg['Subject'] = f"□ SmartLib : {book_title}" msg.attach(MIMEText(f"Livre emprunté : {book_title}", 'plain')) # Attacher le PDF with open(pdf_path, "rb") as f: part = MIMEBase('application', 'octet-stream') part.set_payload(f.read()) encoders.encode_base64(part) msg.attach(part) # Envoi via Gmail SMTP port 587 s = smtplib.SMTP('smtp.gmail.com', 587) s.starttls() s.login(SMTP_EMAIL, SMTP_PASSWORD) s.sendmail(SMTP_EMAIL, DESTINATAIRE, msg.as_string()) s.quit() except Exception as e: logger.error(f"✗ Erreur Email: {e}")
Génération de reçus PDF
La fonction generate_receipt() crée un reçu PDF au format A4 à chaque emprunt. Le fichier est sauvegardé localement ET copié dans le dossier Samba partagé.
def generate_receipt(book_id, borrow_date, due_date, username): conn = sqlite3.connect(DB_PATH); c = conn.cursor() c.execute("SELECT title, author, genre FROM books WHERE id=?", (book_id,)) b = c.fetchone(); conn.close() if not b: return None title, author, genre = b fname = f"recu_emprunt_{book_id}_{datetime.now().strftime('%Y%m%d_%H%M')}.pdf" local = os.path.join(USER_HOME, fname) samba = os.path.join(SAMBAPATH, fname) os.makedirs(SAMBAPATH, exist_ok=True) # Création du PDF A4 pdf = canvas.Canvas(local, pagesize=A4) w, h = A4 pdf.setFont("Helvetica-Bold", 16) pdf.drawString(50, h-50, "■ SmartLib - Reçu") pdf.setFont("Helvetica", 12) pdf.drawString(50, h-70, f"Date : {datetime.now().strftime('%d/%m/%Y %H:%M')}") pdf.drawString(50, h-90, f"Utilisateur : {username}") pdf.drawString(70, h-140, f"Titre : {title}") pdf.drawString(70, h-160, f"Auteur : {author}") pdf.drawString(70, h-180, f"Genre : {genre}") pdf.drawString(70, h-200, f"Emprunté le : {borrow_date}") pdf.drawString(70, h-220, f"À rendre : {due_date}") pdf.save() try: shutil.copy2(local, samba) except Exception as e: print(f"Erreur Samba: {e}") return local, title
Samba + mDNS (Avahi)
Samba permet aux administrateurs d'accéder aux reçus PDF depuis n'importe quel PC du réseau local. mDNS (Avahi) donne un nom de domaine simple au Raspberry Pi.
Configuration /etc/samba/smb.conf
[global] netbios name = smartlib [SmartLib_Receipts] path = /home/abdel/smartlib_receipts browseable = yes writable = yes guest ok = yes read only = no valid users = abdel
Accès depuis Windows
# Dans la barre d'adresse de l'explorateur : \\smartlib.local\SmartLib_Receipts # Ou par IP directe : \\192.168.0.195\SmartLib_Receipts
smartlib.local est résolu grâce au démon Avahi (mDNS) qui diffuse le nom du Raspberry Pi sur le réseau local. Assurez-vous qu'Avahi est actif : sudo systemctl enable avahi-daemonLogging / SysLogHandler
Tous les événements importants sont journalisés dans le syslog système via logging.handlers.SysLogHandler.
def setup_logging(): logger = logging.getLogger("SmartLib") logger.setLevel(logging.INFO) try: handler = logging.handlers.SysLogHandler(address='/dev/log') formatter = logging.Formatter( '%(asctime)s SmartLib: %(levelname)s - %(message)s') handler.setFormatter(formatter) logger.addHandler(handler) except Exception as e: console = logging.StreamHandler() logger.addHandler(console) return logger logger = setup_logging()
Consulter les logs
sudo journalctl -f | grep SmartLib
# Ou :
sudo journalctl -u smartlib -f
| Événement journalisé | Niveau |
|---|---|
| Démarrage en mode Kiosque | INFO |
| Badge RFID détecté (UID) | INFO |
| Emprunt validé (utilisateur + livre) | INFO |
| Nouveau badge enregistré | INFO |
| E-mail envoyé avec PDF | INFO |
| Export CSV effectué | INFO |
| Sauvegarde cloud terminée | INFO |
| Connexion admin réussie | INFO |
| Erreur lecture RFID | ERROR |
| PDF supprimé (nettoyage auto) | INFO |
Cron — Tâches planifiées
Deux tâches Cron s'exécutent automatiquement chaque nuit à 2h du matin.
# m h dom mon dow commande # Sauvegarde quotidienne de la base à 2h02 0 2 * * * cp /home/abdel/smartlib.db /home/abdel/smartlib/backups/smartlib_$(date +\%Y\%m\%d).db # Suppression des backups vieux de +7 jours 0 2 * * * find /home/abdel/smartlib/backups -name 'smartlib_backup_*.db' -mtime +7 -delete
Le nettoyage des PDF expirés (vieux de plus de 30 jours) est géré dans le code Python via la fonction cleanup_old_pdfs(days=30), appelée au démarrage.
Sauvegarde (Local + Cloud)
Deux types de sauvegarde sont disponibles depuis l'interface admin : locale (sur le Raspberry Pi) et cloud (vers AlwaysData via SFTP/Paramiko).
ssh-abdel.alwaysdata.net:22
REMOTE_USER, REMOTE_PASSWORD) sont écrits en clair dans le code source. En production, utiliser des variables d'environnement ou un fichier de configuration protégé.Interface client (Tkinter)
L'interface principale s'ouvre en mode kiosque (plein écran) et contient 3 onglets.
| Onglet | Contenu | Actions disponibles |
|---|---|---|
| Emprunter | Liste complète des livres avec disponibilité, recherche et filtre par genre | Sélectionner un livre → Emprunter (scan badge) |
| Retourner | Emprunts en cours de l'utilisateur avec dates de retour prévues | Sélectionner → Retourner (scan badge) |
| Mon historique | Tous les emprunts passés avec statut (En cours / Rendu) | Consultation uniquement |
Interface administrateur
L'interface admin est accessible via le bouton □ Admin depuis l'écran principal. Elle contient 2 onglets et 8 boutons d'action.
| Bouton | Action |
|---|---|
| + Livre | Ouvre une fenêtre pour ajouter un livre (titre, auteur, genre, quantité) |
| Suppr. | Supprime le livre sélectionné de la base |
| + Ex | Ajoute un exemplaire au livre sélectionné |
| − Ex | Retire un exemplaire du livre sélectionné |
| □ Enregistrer | Scan RFID + saisie nom → enregistre un badge en base |
| Suppr. Badge | Supprime un badge RFID (liste déroulante) |
| □ Backup Local | Copie de smartlib.db dans /backups/ horodatée |
| ▲ Backup Cloud | Upload SFTP de smartlib.db vers AlwaysData |
| CSV | Export de l'historique complet en fichier .csv |
AlwaysData — Sauvegarde cloud
AlwaysData est un hébergeur gratuit utilisé pour stocker les copies distantes de la base de données. L'accès se fait en SSH/SFTP.
REMOTE_HOST = "ssh-abdel.alwaysdata.net" REMOTE_PORT = 22 REMOTE_USER = "abdel" REMOTE_PATH = "/home/abdel/backups/"
Se connecter en SSH (vérification)
ssh abdel@ssh-abdel.alwaysdata.net
# Lister les backups :
ls -lh /home/abdel/backups/
smartlib.db (≈ 36 KB chacun).📄 Rapport TFE complet
Le rapport complet du Travail de Fin d'Études de Bako Abdelwadoud — INRACI Forest, année scolaire 2025–2026. Consultez ou téléchargez le PDF directement ci-dessous.
Abdelwadoud_Bako_TFE_6I.pdf dans le même dossier que ce fichier HTML, ou utilisez le bouton Télécharger.