RFID Raspberry-pi

Intégrer un lecteur RFID à un Raspberry-Pi

Table des matières

Chez Openest, nous réalisons souvent des prototypes rapides en associant des solutions « sur étagère » pour répondre aux besoins de startups. Dans cette optique, nous souhaitons enrichir notre boîte à outils par l’ajout de modules RFID simples à intégrer. Cet article présente notre retour d’expérience pour connecter un lecteur RFID rove 125 kHz à un Raspberry Pi Zero 2 W, ses limites et sa mise en œuvre rapide.

Présentation du module RFID 125 kHz Grove

e module Grove RFID 125 kHz de Seeds studio permet de lire des badges EM4100 (125 kHz) via deux sorties :

  • UART (TTL 9600 bauds, 8N1)
  • Wiegand (26 bits)

Limitation : ce lecteur est très basique : il ne gère pas la lecture simultanée de plusieurs tags, et il est impossible de détecter de manière fiable le retrait d’un tag. Autrement dit, il ne peut traiter qu’un seul tag à la fois et ne fournit pas d’indication lors de son retrait.

Pour autant, cette simplicité se traduit par une mise en œuvre rapide et un coût réduit.

Câblage avec le Raspberry Pi

J’ai monté le module sur un Raspberry Pi Zero 2 W équipé d’une carte Grove Base Hat. Le connecteur Grove fournit VCC, GND, TX et RX :

  • VCC (rouge) → 5 V du Pi (Broche 2 ou 4) (la carte Grove ne délivre que 3,3 V, insuffisant pour le module)
  • GND (noir) → GND du Pi
  • TX (vert) du module → RX (GPIO 15, Pin 10) du Pi
  • RX (blanc) du module ← TX (GPIO 14, Pin 8) du Pi

Pour contourner la limite de 3,3 V, j’ai coupé le fil rouge du connecteur Grove et l’ai relié manuellement à la broche 5 V du Pi.

Configuration du port UART

Par défaut, le Raspberry Pi Zero 2 W réserve /dev/ttyAMA0 au Bluetooth et /dev/ttyS0 comme UART principal. Pour utiliser le port série /dev/ttyAMA0 en tant que liaison UART vers notre module, j’ai activé l’UART matériel. Il faut pour cela éditer le fichier /boot/config.txt et ajouter la ligne suivante :

enable_uart=1

Après un redémarrage, le port série /dev/ttyAMA0 est disponible pour communiquer avec le module via les GPIO 14 et 15 du header du Raspberry-Pi.

Test de lecture simple du lecteur rfid

Pour tester la connexion, j’ai écrit un petit script Python qui lit en boucle les données série du module. Il faut installer la bibliothèque pyserial au préalable (pip install pyserial). Voici un exemple de programme minimal :

import serial

# Configuration du port série
ser = serial.Serial('/dev/ttyAMA0', baudrate=9600, timeout=1)
print("En attente de tag RFID...")

while True:
    data = ser.read(14)  # lire les octets envoyés par le module
    if data:
        tag_id = data.decode('utf-8', errors='ignore').strip()
        print("Tag détecté :", tag_id)


Ce script se met en attente, puis lit les trames de 14 octets envoyées par le module (au format ASCII/hex). Lorsqu’un badge 125 kHz passe devant le capteur, son identifiant s’affiche dans le terminal. Par exemple, on peut voir quelque chose comme :

$ python3 simpletest.py
En attente de tag RFID…
Tag détecté : 6000DB571CF0

Gestion avancée en Python avec thread et callbacks

Pour aller plus loin, j’ai encapsulé la lecture RFID dans une classe Python exécutée dans un thread dédié, afin de pouvoir réagir aux événements de manière asynchrone (tag détecté). L’idée est de lancer en fond une boucle qui interroge le module en permanence. On garde en mémoire le dernier identifiant lu : si on lit un nouvel identifiant différent, cela déclenche le callback on_detect.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
@file    rfid_reader.py
@brief   Threaded RFID reader for Grove 125KHz RFID Reader over UART
"""

import threading
import serial
import sys
import time

class RFIDReader(threading.Thread):
	"""
	@brief  Threaded reader for EM4095-based Grove RFID module
	"""

	def __init__(self, port):
		"""
		@brief          Initialize RFIDReader thread
		@param  port    UART port (e.g. '/dev/ttyAMA0')
		"""
		threading.Thread.__init__(self, daemon=True)
		# variable declarations
		self.port = port
		self.baudrate = 9600 
		self.callbacks = { 'tag_detected': [] }
		self._serial = None
		self._buffer = bytearray()
		self._current_tag = None
		self._last_seen = 0.0
		self._stop_event = threading.Event()
		# open and verify serial
		try:
			self._serial = serial.Serial(
				port=self.port,
				baudrate=self.baudrate,
				bytesize=serial.EIGHTBITS,
				parity=serial.PARITY_NONE,
				stopbits=serial.STOPBITS_ONE,
				timeout=0
			)
		except serial.SerialException as e:
			print(f"Error opening serial port: {e}", file=sys.stderr)
			sys.exit(1)

	def register_callback(self, event, callback):
		"""
		@brief      Register a callback for an event
		@param  callback  Callable(tag_id)
		"""
		if event in self.callbacks:
			self.callbacks[event].append(callback)
		else:
			raise ValueError(f"Unknown event: {event}")

	@staticmethod
	def _extract_tag_from_buffer(buf):
		"""
		@brief      Extract one complete tag frame from buffer
		@param  buf  bytearray reference
		@return     tuple(str tag_id, int consumed)
		"""
		STX = 0x02
		# find STX
		i = buf.find(bytes((STX,)))
		if i == -1 or len(buf) < i + 11:
			return None, 0
		frame = buf[i+1:i+11]
		# decode ASCII hex
		try:
			tag_str = frame.decode('ascii')
		except UnicodeDecodeError:
			return None, i+1
		# calculate consumed bytes including STX + frame
		consumed = i + 11
		# skip ETX/CR/LF
		while consumed < len(buf) and buf[consumed] in (0x03, 0x0D, 0x0A):
			consumed += 1
		return tag_str, consumed

	def run(self):
		"""
		@brief  Thread main loop: read serial and dispatch events
		"""
		while not self._stop_event.is_set():
			try:
				n = self._serial.in_waiting
				if n:
					data = self._serial.read(n)
					self._buffer.extend(data)
					# process all complete tags
					while True:
						tag, consumed = self._extract_tag_from_buffer(self._buffer)
						if not tag:
							break
						# remove consumed bytes
						del self._buffer[:consumed]
						# new tag detected
						self._last_seen = time.time()
						if tag != self._current_tag:
							self._current_tag = tag
							for cb in self.callbacks['tag_detected']:
								cb(tag)
				# brief pause
				time.sleep(0.01)
			except Exception as e:
				# handle serial read errors
				print(f"Error in RFIDReader thread: {e}", file=sys.stderr)
				self.stop()

	def stop(self):
		"""
		@brief  Signal thread to stop and close serial
		"""
		self._stop_event.set()
		if self._serial and self._serial.is_open:
			self._serial.close()

On peut ensuite utiliser cette classe dans un programme comme ceci:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
@file    example.py
@brief   Example usage of RFIDReader
"""

import time
from rfid_reader import RFIDReader

def on_tag_detected(tag_id):
	print(f"[EVENT] Tag detected: {tag_id}")

def main():
	"""
	@brief  Launch RFIDReader and register callbacks
	"""
	# Initialize reader on UART port
	reader = RFIDReader(port='/dev/ttyAMA0')
	# Register event callbacks
	reader.register_callback('tag_detected', on_tag_detected)
	# Start reader thread
	reader.start()
	print("Reader thread started. Approach a tag...")
	try:
		# Keep main thread alive
		while True:
			time.sleep(1)
	except KeyboardInterrupt:
		print("\nStopping reader...")
		reader.stop()
		reader.join()
		print("Reader stopped.")

if __name__ == "__main__":
	main()

Conclusion sur ce lecteur RFID pour Raspberry-Pi

En suivant ces étapes, j’ai pu intégrer le lecteur Grove 125 kHz à mon Raspberry Pi Zero 2 W dans un prototype fonctionnel. Le principal obstacle était l’alimentation du module (nécessitant 5V), contourné en modifiant le câble Grove. Ensuite, la configuration de l’UART et le code Python (lecture simple ou gestion via thread) ont permis d’obtenir un flux de données stable. Ce retour d’expérience montre qu’avec quelques adaptations matérielles et un peu de code, nous pourrons intégrer ce module RFID dans des projets de prototypage.

Ce module lecteur RFID Grove 125 kHz combiné à un Raspberry-Pi, malgré sa simplicité (lecture mono-tag, absence d’événement de retrait), se révèle efficace et rapide à déployer pour des prototypes rapides. Chez Openest, il enrichit notre boîte à outils pour des démonstrations et POCs avant de faire évoluer ces prototypes vers des solutions industrialisables (avec NFC, UHF, multi-antennes, RSSI, etc.) mais qui necessites souvent d’évaluer des solutions plus complexes.

Quels types de tags RFID sont compatibles avec ce module ?

Le lecteur Grove 125 kHz gère principalement les tags EM4100 ou compatibles 125 kHz. Ces tags sont courants, peu chers et adaptés pour des prototypes simples.

Peut-on détecter plusieurs tags en même temps ?

Non, la puce EM4095 du module ne supporte pas l’anti-collision. Le module ne peut traiter qu’un seul tag à la fois. Placer plusieurs tags simultanément dans la zone de lecture conduit à des trames corrompues ou indétectables.

Pourquoi je ne peux pas détecter le retrait d’un tag de façon fiable ?

Le module n’émet des trames UART que lorsqu’il lit un tag. En l’absence de tag, il cesse simplement d’envoyer de données, sans signal explicite.

Peut-on utiliser ce lecteur sur d’autres plateformes (Arduino, ESP32) ?

Oui, la sortie UART TTL est universelle. Il suffit de connecter TX/RX à un port série et d’adapter le code. De nombreux exemples Arduino/ESP32 existent pour la puce EM4095. Seeedsudio fourni une page de wiki à ce sujet.

Partager cet article

Suivez-nous et ne manquez aucun article !

Besoin d'aide pour prototyper votre objet connecté ?

Openest est un bureau d’ingénierie logicielle spécialisé dans le prototypage d’objets connectés intelligents.

Bénéficiez de notre expertise du prototypage

bureau d'études agréé crédit impot innovation

Écrivez-nous un message et discutons de votre projet !