Système de caisse automatique NFC sur Raspberry Pi
Un projet de fin d'études ambitieux qui reproduit fidèlement le fonctionnement d'une caisse automatique moderne — gestion de produits, clients, paiements sécurisés, sauvegardes automatiques et déploiement physique sur Raspberry Pi 4.
IT-WORLD est un projet de TFE (Travail de Fin d'Études) réalisé dans le cadre de la formation en informatique. Il simule de façon très complète une caisse automatique réelle.
Concevoir, développer et déployer une caisse automatique fonctionnelle, capable de gérer des produits, des clients, des paiements et des tickets — le tout sur un Raspberry Pi 4 avec un écran tactile.
Inspiré des systèmes utilisés dans les grandes surfaces (Carrefour, Delhaize…), IT-WORLD en reproduit la logique métier, l'interface utilisateur et la gestion des données, en version compacte et autonome.
Une borne de caisse entièrement opérationnelle, avec interface graphique plein écran, base de données locale, simulation de paiement Stripe, module NFC et génération de ticket à chaque transaction.
Les caisses automatiques sont omniprésentes dans notre quotidien, mais leur fonctionnement interne reste peu documenté de manière accessible. IT-WORLD est né de la volonté de comprendre et de reproduire ces systèmes complexes, en partant de zéro.
L'ambition était claire : ne pas se contenter d'une simulation à l'écran, mais réaliser un vrai déploiement physique sur matériel embarqué, avec toutes les contraintes que cela implique.
Interface fonctionnelle sur écran tactile, base de données locale sans serveur distant, compatibilité Raspberry Pi OS, et performance suffisante sur un hardware limité (4 Go RAM, CPU ARM).
Python pour la logique, Tkinter pour l'interface (léger et natif), SQLite pour la persistance des données, Stripe API pour les paiements, et Git/GitHub pour le versioning.
Le client utilise la borne en mode fullscreen pour scanner ses articles, choisir son mode de paiement et recevoir son ticket.
L'administrateur accède à un panneau dédié pour gérer le catalogue de produits, consulter les statistiques de vente, exporter les données et configurer le système.
hashlib.sha256.L'un des aspects les plus innovants du projet est l'intégration d'un lecteur NFC (RC522) connecté au Raspberry Pi via le bus SPI. Chaque client peut être associé à un badge ou une carte NFC.
Au passage de la carte, le système identifie automatiquement le client, charge son panier habituel si activé, et pré-remplit ses informations de profil.
# Lecture d'un badge NFC via RC522 import RPi.GPIO as GPIO from mfrc522 import SimpleMFRC522 reader = SimpleMFRC522() def lire_badge(): try: uid, texte = reader.read_no_block() if uid: return str(uid).strip() return None finally: GPIO.cleanup()
Chaque fonctionnalité a été pensée pour reproduire fidèlement les systèmes professionnels, avec les contraintes d'un matériel embarqué.
Tout le code de l'interface est centralisé dans interface.py. La fenêtre principale (root) est créée avec Tkinter, forcée en plein écran dès le démarrage, et cachée (root.withdraw()) pendant que la popup de sélection du type de client s'affiche.
Dès que le client fait son choix (client existant, création de compte, mode professionnel ou invité), la fenêtre principale est révélée via root.deiconify(). L'interface affiche ensuite en temps réel le panier via un Treeview ttk, le sous-total, la TVA 21% et le total — ou le montant HTVA pour les clients professionnels.
fullscreen, fond #101010, thème personnalisé ttk.Style<Return>root.after(1000, ...)Ctrl+Shift+Awinsound.Beep si disponible# Fenêtre principale — masquée au démarrage root = tk.Tk() root.withdraw() root.attributes("-fullscreen", True) root.configure(bg="#101010") root.bind("<Escape>", lambda e: root.destroy()) root.bind("<Control-Shift-A>", lambda e: open_admin_login()) # Champ de scan invisible (capte le lecteur NFC) entry_uid = tk.Entry(root) entry_uid.place(x=-100, y=-100) entry_uid.bind("<Return>", scan_product) # Révéler l'interface après choix du client def show_main_interface(): root.deiconify() # Horloge en temps réel def update_clock(): now = datetime.now().strftime( "%d/%m/%Y - %H:%M:%S") clock_label.config(text=now) root.after(1000, update_clock)
entry_uid est positionné à x=-100, y=-100 pour rester invisible tout en conservant le focus clavier — technique classique pour capturer les lectures NFC qui simulent une saisie clavier.Toutes les données du projet sont stockées dans une base SQLite locale appelée shop.db, sans aucun serveur externe. La connexion est établie dès le démarrage de main.py et reste ouverte pendant toute la durée de fonctionnement de la caisse.
Trois tables sont créées automatiquement au premier lancement avec CREATE TABLE IF NOT EXISTS, ce qui garantit qu'elles n'existent qu'une seule fois même si le programme est relancé. Chaque ajout au panier déclenmente directement le stock en base via un UPDATE atomique suivi d'un commit().
products : uid (clé NFC), name, price, stock, imagetickets : id autoincrement, total, date de la transactionclients : phone unique, client_number généré aléatoirement, tva_numberORDER BY id DESC LIMIT 10import sqlite3 conn = sqlite3.connect("shop.db") cursor = conn.cursor() # Création automatique des 3 tables au démarrage cursor.execute(""" CREATE TABLE IF NOT EXISTS products ( uid TEXT PRIMARY KEY, name TEXT, price REAL, stock INTEGER, image TEXT )""") cursor.execute(""" CREATE TABLE IF NOT EXISTS tickets ( id INTEGER PRIMARY KEY AUTOINCREMENT, total REAL, date TEXT )""") cursor.execute(""" CREATE TABLE IF NOT EXISTS clients ( id INTEGER PRIMARY KEY AUTOINCREMENT, phone TEXT UNIQUE, client_number TEXT UNIQUE, tva_number TEXT )""") conn.commit() # Scan : décrémente le stock et retourne les infos def add_product(uid): cursor.execute( "SELECT name,price,stock,image FROM products WHERE uid=?", (uid,)) product = cursor.fetchone() if product and product[2] > 0: cursor.execute( "UPDATE products SET stock=stock-1 WHERE uid=?", (uid,)) conn.commit() return True, {"name": product[0], "price": product[1], "image": product[3]} return False, {"error": "rupture"}
products — chaque badge NFC est donc directement lié à un produit en base, sans couche intermédiaire.L'intégration Stripe est gérée directement dans interface.py. Quand le client clique sur "PAYER", la fonction open_payment() calcule le total via main.get_total_with_tva(), puis crée une Checkout Session Stripe avec le montant exact converti en centimes.
Stripe retourne une URL de paiement sécurisée qui s'ouvre automatiquement dans le navigateur via webbrowser.open(). Une fois le paiement effectué, le ticket est enregistré en base via main.add_ticket(total) et le panier est vidé. Tout est encapsulé dans un try/except pour afficher un message d'erreur propre en cas de problème Stripe.
stripe.checkout.Session avec montant en centimespayment_method_types=["card"])except Exception avec messageboxsk_test_...) — aucun vrai débitimport stripe import webbrowser stripe.api_key = "sk_test_51Sydzo..." def open_payment(): total = main.get_total_with_tva() if total == 0: messagebox.showwarning("Paiement", "Panier vide") return try: session = stripe.checkout.Session.create( payment_method_types=["card"], line_items=[{ "price_data": { "currency": "eur", "product_data": { "name": "Paiement IT WORLD" }, "unit_amount": int(total * 100), }, "quantity": 1, }], mode="payment", success_url="https://example.com/success", cancel_url="https://example.com/cancel", ) webbrowser.open(session.url) main.add_ticket(total) reset_cart() except Exception as e: messagebox.showerror("Erreur Stripe", str(e))
1250. Pour les clients professionnels, get_total_with_tva() retourne le montant HTVA directement.La fonction create_backup() est définie dans main.py et appelée automatiquement à chaque démarrage du programme. Elle crée une copie horodatée du fichier shop.db dans le dossier backups/, qui est créé s'il n'existe pas encore.
Le nom du fichier de backup inclut la date et l'heure précises au format backup_YYYY-MM-DD_HH-MM-SS.db, ce qui permet de retrouver facilement une version précise en cas de problème. La copie est réalisée avec shutil.copy(), fonction standard Python qui garantit une copie binaire fidèle du fichier SQLite.
backups/ créé automatiquement si absentbackup_2026-05-29_14-32-10.dbshutil.copy() — rapide et fiableshop.db avant copieimport shutil import os from datetime import datetime def create_backup(): backup_folder = "backups" # Crée le dossier s'il n'existe pas if not os.path.exists(backup_folder): os.makedirs(backup_folder) # Nom horodaté : backup_2026-05-29_14-32-10.db now = datetime.now().strftime( "%Y-%m-%d_%H-%M-%S") backup_name = f"backup_{now}.db" backup_path = os.path.join( backup_folder, backup_name ) # Copie uniquement si la BDD existe if os.path.exists("shop.db"): shutil.copy("shop.db", backup_path) # Appelé automatiquement au démarrage create_backup()
backups/ accumule ainsi un historique complet des états de la base, consultable directement avec DB Browser for SQLite (.sqbpro).Retour détaillé sur chaque phase du projet, des premières lignes de code jusqu'au déploiement physique final sur Raspberry Pi 4.
Développement de l'interface Tkinter fullscreen et mise en place de la base de données SQLite. Création du popup client (4 modes), du panier avec Treeview, du calcul TVA automatique et du système de backup au démarrage.
root.withdraw() + popup clientLe module PN532 prévu initialement ne répondait pas malgré de nombreux essais de câblage et de configuration. Cette difficulté a causé une perte de temps importante avant de décider de le remplacer par le RC522.
root.after(0, lambda u=uid: scan_product(u))products pour retrouver le produit correspondant.Intégration du paiement Stripe en mode Checkout Session et génération automatique d'un ticket PDF via ReportLab après chaque paiement.
lp -d CanonTS3351Refonte complète du panel administrateur avec 3 onglets distincts utilisant ttk.Notebook.
Configuration complète du déploiement sur le Pi avec autostart, impression réseau et gestion des permissions Linux.
/home/danny/.config/autostart/itworld.desktopnmap 192.168.129.44sudo chown -R danny:danny /home/pi/IT_WORLDupdate_uids.pyattributes(-fullscreen) + lift()Captures d'écran et photos du projet en fonctionnement réel sur Raspberry Pi 4.
Vue d'ensemble de la stack technique et du flux de données, du client jusqu'à la génération du ticket.
Cœur du système. Fait tourner Python, SQLite, les services système et le gestionnaire de fenêtres Xorg pour Tkinter.
N'importe quel écran compatible peut être utilisé. L'interface Tkinter s'adapte à la résolution disponible et passe automatiquement en mode plein écran au lancement.
Module de lecture NFC connecté au Raspberry Pi. Permet la détection instantanée de badges ou cartes sans contact pour l'identification des clients.
Système d'exploitation + base de données + sauvegardes. Vitesse d'écriture suffisante pour SQLite en charge normale.
itworld/ ├── __pycache__/ # Cache bytecode Python (généré auto) ├── backups/ # Sauvegardes automatiques de la BDD ├── images/ # Images produits, logo, assets ├── interface.py # Interface Tkinter complète (popup client, scan, panier, admin) ├── main.py # Logique métier : BDD, panier, Stripe, clients ├── shop.sqbpro # Fichier projet DB Browser (SQLite) └── shop # Base de données SQLite principale
Chaque technologie a été sélectionnée avec soin pour répondre aux contraintes du projet : légèreté, compatibilité ARM, fonctionnement hors-ligne.
Travail de Fin d'Études réalisé par Danny Hurst — 6TQ Informatique — INRACI — Année scolaire 2025-2026
Système de caisse automatique NFC — Pour l'obtention du certificat de qualification Technicien(ne) en informatique.
Encadré par Monsieur Janah et Monsieur Ben Sellam — Institut National de Radioélectricité et de Cinématographie (INRACI), Forest.
2025 — 2026. Développé sur 3 mois environ, de février à juin 2026. Déploiement final sur Raspberry Pi 4 avec imprimante Canon TS3351.
Le rapport définit la problématique principale : "Comment développer une caisse automatique moderne, fonctionnelle et intuitive en utilisant des technologies accessibles ?"
Les technologies retenues sont justifiées par leur légèreté, compatibilité ARM et gratuité : Python, Tkinter, SQLite, Stripe, Raspberry Pi.
Le rapport documente honnêtement les obstacles rencontrés, notamment la difficulté majeure du module PN532 qui n'a pas pu être intégré dans les délais.
root.after()Le rapport TFE complet (39 pages) inclut l'analyse des besoins, la conception, le développement, les schémas, les résultats et la bibliographie complète.
📥 Télécharger le rapport TFE (PDF)Hurst Danny — INRACI — 6TQ Informatique — 2025-2026