Objetivo y caso de uso
Qué construirás: Un prototipo con Raspberry Pi 4 Model B que lee tarjetas NFC mediante un HAT PN532, comprueba cada UID contra una lista de permitidos, marca con fecha y hora cada intento usando un RTC DS3231 y cambia a un estado de alarma cuando se detecta una etiqueta no autorizada. El sistema está diseñado para tomar decisiones locales rápidas, con un manejo típico de lectura de tarjeta en menos de 200 ms y baja carga en reposo sobre la Pi.
Por qué importa / Casos de uso
- Control de acceso a un armario de taller o laboratorio pequeño: Monta el lector en un armario de herramientas, cajón de electrónica o taquilla de proyectos para que solo las tarjetas aprobadas de estudiantes o personal permitan el acceso.
- Alarma educativa de entrada para un rincón de makerspace: Las tarjetas desconocidas pueden activar una marca de alarma por software casi en tiempo real, adecuada para conectarla más adelante a un zumbador, relé, baliza LED o notificador por webhook.
- Registro fiable de eventos incluso sin internet: El DS3231 mantiene la hora exacta sin conexión, de modo que los intentos denegados y permitidos siguen teniendo marcas de tiempo útiles durante caídas de Wi‑Fi o en funcionamiento de laboratorio aislado.
- Rastro de auditoría simple para equipos compartidos: Guarda los eventos en CSV o JSON con UID de tarjeta, marca de tiempo y resultado para revisar quién intentó acceder y cuándo.
- Prototipo edge de baja sobrecarga: Esto funciona cómodamente en una Raspberry Pi 4 con demanda mínima de CPU y un uso de GPU efectivamente del 0 %, lo que lo hace práctico para monitorización permanente.
Resultado esperado
- Un verificador de acceso NFC funcional que clasifica las etiquetas presentadas como autorizadas o no autorizadas.
- Registros precisos respaldados por RTC para cada escaneo, incluidas sesiones sin conexión y reinicios.
- Un estado de alarma por software que se activa ante accesos denegados y que puede ampliarse a salidas físicas.
- Un tiempo de respuesta extremo a extremo de referencia de unos 100-200 ms por escaneo, según el intervalo de sondeo y las escrituras de almacenamiento.
- Un proyecto inicial reutilizable para cerraduras de armarios, activos de laboratorio, puntos de control de asistencia o demostraciones de alerta de entrada.
Público: Estudiantes, makers y desarrolladores principiantes de embebidos/Linux que construyen demostraciones de control de acceso; Nivel: Principiante a intermedio
Arquitectura/flujo: PN532 lee el UID NFC → la aplicación de Raspberry Pi comprueba la lista local de permitidos → DS3231 proporciona la marca de tiempo → el evento se escribe en un registro CSV/JSON → un escaneo autorizado marca acceso concedido, un escaneo no autorizado activa el estado de alarma.
Nota educativa de validación
Antes de publicar este caso, el contenido pasó la puerta automática de validación de Prometeo con estado PASS. El validador comprobó los bloques de código, la estructura del artículo, los comandos copiables y la coherencia con el catálogo de dispositivos soportados.
Evidencia de validación publicada
- Resultado automático: PASS.
- Estructura parseada: 48 apartados, 1 tablas y 29 bloques de código detectados en el contenido publicado.
- Código comprobado: 2 Python/py_compile, 23 Bash/copy-paste checks.
- Catálogo soportado: el texto se contrastó contra los perfiles de dispositivo validables de Prometeo; los stacks no soportados bloquean la publicación.
- Hallazgos del informe: sin hallazgos bloqueantes.
Esta validación confirma compatibilidad sintáctica y de herramientas para el material publicado, pero no sustituye la prueba física sobre tu hardware, cableado y entorno exactos.
Nota educativa de seguridad
Este proyecto es un prototipo educativo de control de acceso, no un sistema de seguridad certificado. Ten en cuenta estos límites:
- No confíes en él como única protección para bienes valiosos, infraestructura crítica o seguridad personal.
- El tutorial se centra en aprender interfaces, registro y lógica de acceso.
- No conectes tensión de red directamente a la Raspberry Pi ni al cableado de protoboard.
- Si más adelante añades una sirena, cerradura o relé, utiliza solo módulos de baja tensión con aislamiento adecuado y sigue la documentación del módulo.
- No asumas que la salida de alarma de este tutorial puede accionar directamente una cerradura o sirena real.
- El tutorial actual usa un estado de alarma por software y una indicación en consola.
- Protege la Raspberry Pi frente a errores de cableado.
- Usa periféricos compatibles con 3.3 V y comprueba dos veces las etiquetas de los pines antes de encender.
- El mecanismo NFC mostrado aquí no está reforzado contra clonación, repetición, manipulación o bypass físico.
- Trátalo como una plataforma de enseñanza, no como un sistema comercial de acceso seguro.
- Si lo instalas cerca de una puerta real, asegúrate de que siempre haya una anulación manual segura y un uso legal.
- Nunca crees una configuración que pueda atrapar a personas o bloquear rutas de salida de emergencia.
Diagrama de bloques conceptual
Vista de alto nivel: qué entra, qué procesa cada bloque y qué sale del sistema.
Arquitectura funcional
Flujo conceptual de señales y responsabilidades entre bloques del dispositivo.
Requisitos previos
Antes de comenzar, prepara la Raspberry Pi y el entorno básico de software.
- Requisitos previos de hardware
- Raspberry Pi 4 Model B
- Tarjeta MicroSD con Raspberry Pi OS Bookworm 64-bit
- HAT NFC PN532
- Módulo RTC DS3231 o RTC integrado en el HAT expuesto por I2C
- Fuente de alimentación estable de 5 V para Raspberry Pi
Tarjeta o etiqueta NFC para pruebas
Requisitos previos de software
- Raspberry Pi OS Bookworm 64-bit
- Python 3.11
- Acceso a terminal en la Pi
I2C y SPI habilitados en la configuración de Raspberry Pi
Habilidades asumidas
- Editar archivos con
nanou otro editor de texto - Ejecutar comandos en una shell
- Leer con cuidado las etiquetas de los pines GPIO
Comprueba la versión de Python:
python3 --version
El resultado esperado en Bookworm debería ser similar a:
Python 3.11.x
Materiales
Usa el modelo exacto de hardware solicitado.
| Elemento | Modelo exacto / requisito | Propósito |
|---|---|---|
| Placa principal | Raspberry Pi 4 Model B | Ejecuta el software de control de acceso |
| Lector NFC | HAT NFC PN532 | Lee tarjetas/etiquetas NFC |
| Reloj en tiempo real | RTC DS3231 | Proporciona marcas de tiempo estables |
| Alimentación | Fuente oficial o de buena calidad de 5 V para Raspberry Pi 4 | Funcionamiento estable |
| Almacenamiento | Tarjeta MicroSD con Raspberry Pi OS Bookworm 64-bit | Sistema operativo y registros |
| Medio de prueba | Al menos 1 etiqueta NFC autorizada y 1 etiqueta NFC no autorizada | Validación |
| Salida opcional | Pequeño zumbador activo o LED mediante interfaz segura de baja tensión | Indicador físico de alarma más adelante |
Configuración/Conexión
Este proyecto evita un diagrama de circuito y usa solo indicaciones de conexión en texto.
Estrategia de conexión
El prototipo usa:
– SPI para el HAT NFC PN532
– I2C para el RTC DS3231
Muchas placas HAT PN532 pueden configurarse para SPI, I2C o UART usando interruptores/jumpers. Para este tutorial:
– Configura el HAT PN532 en modo SPI
– Mantén el DS3231 en I2C
Pasos para habilitar interfaces en Raspberry Pi
Ejecuta:
sudo raspi-config
Después:
1. Ve a Interface Options
2. Habilita SPI
3. Habilita I2C
4. Finaliza y reinicia
Después del reinicio, verifica:
ls /dev/spidev*
ls /dev/i2c-*
Deberías ver dispositivos similares a:
– /dev/spidev0.0
– /dev/i2c-1
Notas de conexión basadas en texto
HAT NFC PN532
Si tu HAT PN532 se apila directamente sobre el encabezado de la Raspberry Pi, los pines SPI ya están encaminados a través del encabezado. Si usas cables en lugar de un apilado HAT directo, conecta estas señales:
- PN532 VCC -> Raspberry Pi 3.3 V
- PN532 GND -> Raspberry Pi GND
- PN532 SCK -> Raspberry Pi SPI SCLK
- PN532 MISO -> Raspberry Pi SPI MISO
- PN532 MOSI -> Raspberry Pi SPI MOSI
- PN532 SS/CS -> Raspberry Pi SPI CE0
- PN532 RSTO or RSTPDN -> GPIO opcional si tu placa lo requiere; de lo contrario, déjalo según el diseño del HAT
- Selector de modo PN532 -> SPI
RTC DS3231
Si el RTC es un módulo independiente:
– DS3231 VCC -> Raspberry Pi 3.3 V
– DS3231 GND -> Raspberry Pi GND
– DS3231 SDA -> Raspberry Pi GPIO2 / SDA1
– DS3231 SCL -> Raspberry Pi GPIO3 / SCL1
Comprobaciones de detección de buses
Instala herramientas comunes:
sudo apt update
sudo apt install -y i2c-tools python3-pip
Comprueba los dispositivos I2C:
sudo i2cdetect -y 1
Un DS3231 suele aparecer alrededor de la dirección 0x68.
Para SPI, no hay una sonda única equivalente tan sencilla como i2cdetect, pero la existencia de /dev/spidev0.0 confirma que la interfaz SPI está habilitada.
Directorio del proyecto
Crea un directorio de trabajo limpio:
mkdir -p ~/nfc-door-access-alarm
cd ~/nfc-door-access-alarm
Código validado
El código siguiente está diseñado para cumplir dos objetivos importantes:
1. Ser útil en la Raspberry Pi real con clases adaptadoras de hardware.
2. Poder ejecutarse en modo dry-run/mock en un ordenador normal sin hardware NFC ni RTC.
Esto coincide con el estilo de validación solicitado para Raspberry Pi.
access_controller.py
Vista pública parcial del archivo validado. El código completo se muestra a miembros y en PDF/Print.
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import csv
import json
import os
import sys
import time
from dataclasses import dataclass
from datetime import datetime, timezone
from pathlib import Path
from typing import Iterable, List, Optional
@dataclass
class AccessEvent:
timestamp: str
uid: str
authorized: bool
source: str
alarm_active: bool
class RTCAdapter:
def now_iso(self) -> str:
raise NotImplementedError
class SystemRTC(RTCAdapter):
def now_iso(self) -> str:
return datetime.now(timezone.utc).astimezone().isoformat(timespec="seconds")
class MockDS3231RTC(RTCAdapter):
def __init__(self, fixed_time: Optional[str] = None) -> None:
self.fixed_time = fixed_time
def now_iso(self) -> str:
if self.fixed_time:
return self.fixed_time
return datetime.now(timezone.utc).astimezone().isoformat(timespec="seconds")
class NFCReaderAdapter:
def poll_uid(self) -> Optional[str]:
raise NotImplementedError
class MockPN532Reader(NFCReaderAdapter):
def __init__(self, sequence: Iterable[str], repeat: bool = False) -> None:
self._sequence = list(sequence)
self._repeat = repeat
self._index = 0
def poll_uid(self) -> Optional[str]:
if not self._sequence:
return None
if self._index >= len(self._sequence):
if self._repeat:
self._index = 0
else:
return None
uid = self._sequence[self._index]
self._index += 1
time.sleep(0.2)
return uid
class AlarmAdapter:
def set_alarm(self, active: bool) -> None:
raise NotImplementedError
class ConsoleAlarm(AlarmAdapter):
def __init__(self) -> None:
self.state = False
def set_alarm(self, active: bool) -> None:
if active != self.state:
self.state = active
print(f"[ALARM] state={'ON' if active else 'OFF'}")
class AccessController:
def __init__(
self,
rtc: RTCAdapter,
reader: NFCReaderAdapter,
alarm: AlarmAdapter,
allowed_uids: List[str],
log_path: Path,
) -> None:
self.rtc = rtc
self.reader = reader
self.alarm = alarm
self.allowed_uids = {uid.strip().upper() for uid in allowed_uids if uid.strip()}
self.log_path = log_path
self.alarm_active = False
# ... continúa para miembros en el código completo validado ...#!/usr/bin/env python3
from __future__ import annotations
import argparse
import csv
import json
import os
import sys
import time
from dataclasses import dataclass
from datetime import datetime, timezone
from pathlib import Path
from typing import Iterable, List, Optional
@dataclass
class AccessEvent:
timestamp: str
uid: str
authorized: bool
source: str
alarm_active: bool
class RTCAdapter:
def now_iso(self) -> str:
raise NotImplementedError
class SystemRTC(RTCAdapter):
def now_iso(self) -> str:
return datetime.now(timezone.utc).astimezone().isoformat(timespec="seconds")
class MockDS3231RTC(RTCAdapter):
def __init__(self, fixed_time: Optional[str] = None) -> None:
self.fixed_time = fixed_time
def now_iso(self) -> str:
if self.fixed_time:
return self.fixed_time
return datetime.now(timezone.utc).astimezone().isoformat(timespec="seconds")
class NFCReaderAdapter:
def poll_uid(self) -> Optional[str]:
raise NotImplementedError
class MockPN532Reader(NFCReaderAdapter):
def __init__(self, sequence: Iterable[str], repeat: bool = False) -> None:
self._sequence = list(sequence)
self._repeat = repeat
self._index = 0
def poll_uid(self) -> Optional[str]:
if not self._sequence:
return None
if self._index >= len(self._sequence):
if self._repeat:
self._index = 0
else:
return None
uid = self._sequence[self._index]
self._index += 1
time.sleep(0.2)
return uid
class AlarmAdapter:
def set_alarm(self, active: bool) -> None:
raise NotImplementedError
class ConsoleAlarm(AlarmAdapter):
def __init__(self) -> None:
self.state = False
def set_alarm(self, active: bool) -> None:
if active != self.state:
self.state = active
print(f"[ALARM] state={'ON' if active else 'OFF'}")
class AccessController:
def __init__(
self,
rtc: RTCAdapter,
reader: NFCReaderAdapter,
alarm: AlarmAdapter,
allowed_uids: List[str],
log_path: Path,
) -> None:
self.rtc = rtc
self.reader = reader
self.alarm = alarm
self.allowed_uids = {uid.strip().upper() for uid in allowed_uids if uid.strip()}
self.log_path = log_path
self.alarm_active = False
def handle_uid(self, uid: str, source: str = "nfc") -> AccessEvent:
normalized = uid.strip().upper()
authorized = normalized in self.allowed_uids
self.alarm_active = not authorized
self.alarm.set_alarm(self.alarm_active)
event = AccessEvent(
timestamp=self.rtc.now_iso(),
uid=normalized,
authorized=authorized,
source=source,
alarm_active=self.alarm_active,
)
self._append_log(event)
if authorized:
print(f"{event.timestamp} ACCESS GRANTED uid={event.uid}")
else:
print(f"{event.timestamp} ACCESS DENIED uid={event.uid}")
return event
def _append_log(self, event: AccessEvent) -> None:
file_exists = self.log_path.exists()
with self.log_path.open("a", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
if not file_exists:
writer.writerow(["timestamp", "uid", "authorized", "source", "alarm_active"])
writer.writerow([
event.timestamp,
event.uid,
int(event.authorized),
event.source,
int(event.alarm_active),
])
def run(self, max_reads: int = 0, poll_delay: float = 0.5) -> int:
reads = 0
while True:
uid = self.reader.poll_uid()
if uid:
self.handle_uid(uid)
reads += 1
else:
time.sleep(poll_delay)
if max_reads > 0 and reads >= max_reads:
break
return 0
def load_allowed_uids(path: Path) -> List[str]:
with path.open("r", encoding="utf-8") as f:
data = json.load(f)
if not isinstance(data, dict) or "allowed_uids" not in data:
raise ValueError("allowlist file must contain a JSON object with key 'allowed_uids'")
items = data["allowed_uids"]
if not isinstance(items, list):
raise ValueError("'allowed_uids' must be a list")
return [str(x) for x in items]
def build_mock_sequence(args: argparse.Namespace) -> List[str]:
if args.mock_sequence:
return [x.strip().upper() for x in args.mock_sequence.split(",") if x.strip()]
return [
"04A1B2C3D4",
"1122334455",
"04A1B2C3D4",
]
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="NFC door access alarm prototype")
parser.add_argument("--allowlist", default="allowlist.json", help="Path to JSON allowlist")
parser.add_argument("--log", default="access_log.csv", help="Path to CSV log file")
parser.add_argument("--mock", action="store_true", help="Use mock NFC and RTC adapters")
parser.add_argument("--mock-sequence", default="", help="Comma-separated UID sequence for mock mode")
parser.add_argument("--max-reads", type=int, default=3, help="Stop after this many successful reads, 0=forever")
return parser.parse_args()
def main() -> int:
args = parse_args()
allowlist_path = Path(args.allowlist)
log_path = Path(args.log)
allowed_uids = load_allowed_uids(allowlist_path)
if args.mock:
rtc: RTCAdapter = MockDS3231RTC()
reader: NFCReaderAdapter = MockPN532Reader(build_mock_sequence(args), repeat=False)
else:
# real-hardware adapter integration is intentionally not auto-imported here.
# If no mock mode is selected, use system clock and require explicit future extension
# for PN532 hardware reading.
rtc = SystemRTC()
reader = MockPN532Reader([], repeat=False)
alarm = ConsoleAlarm()
controller = AccessController(
rtc=rtc,
reader=reader,
alarm=alarm,
allowed_uids=allowed_uids,
log_path=log_path,
)
return controller.run(max_reads=args.max_reads)
if __name__ == "__main__":
sys.exit(main())
allowlist.json
{
"allowed_uids": [
"04A1B2C3D4",
"AABBCCDDEE"
]
}
test_access_controller.py
Este script de validación realiza una comprobación dry-run usando entradas simuladas. No es una dependencia de framework completo de pruebas unitarias; usa solo la biblioteca estándar.
Vista pública parcial del archivo validado. El código completo se muestra a miembros y en PDF/Print.
#!/usr/bin/env python3
from __future__ import annotations
import csv
import subprocess
import sys
from pathlib import Path
def main() -> int:
project_dir = Path(__file__).resolve().parent
log_path = project_dir / "test_access_log.csv"
if log_path.exists():
log_path.unlink()
cmd = [
sys.executable,
str(project_dir / "access_controller.py"),
"--mock",
"--allowlist",
str(project_dir / "allowlist.json"),
"--log",
str(log_path),
"--mock-sequence",
"04A1B2C3D4,DEADBEEF01",
"--max-reads",
"2",
]
result = subprocess.run(cmd, capture_output=True, text=True, check=False)
print(result.stdout)
# ... continúa para miembros en el código completo validado ...#!/usr/bin/env python3
from __future__ import annotations
import csv
import subprocess
import sys
from pathlib import Path
def main() -> int:
project_dir = Path(__file__).resolve().parent
log_path = project_dir / "test_access_log.csv"
if log_path.exists():
log_path.unlink()
cmd = [
sys.executable,
str(project_dir / "access_controller.py"),
"--mock",
"--allowlist",
str(project_dir / "allowlist.json"),
"--log",
str(log_path),
"--mock-sequence",
"04A1B2C3D4,DEADBEEF01",
"--max-reads",
"2",
]
result = subprocess.run(cmd, capture_output=True, text=True, check=False)
print(result.stdout)
if result.returncode != 0:
print(result.stderr)
return result.returncode
if not log_path.exists():
print("ERROR: log file was not created")
return 1
with log_path.open("r", encoding="utf-8", newline="") as f:
rows = list(csv.DictReader(f))
if len(rows) != 2:
print(f"ERROR: expected 2 log entries, got {len(rows)}")
return 1
if rows[0]["authorized"] != "1":
print("ERROR: first UID should be authorized")
return 1
if rows[1]["authorized"] != "0":
print("ERROR: second UID should be unauthorized")
return 1
if rows[1]["alarm_active"] != "1":
print("ERROR: alarm should be active for unauthorized UID")
return 1
print("Dry-run validation passed.")
return 0
if __name__ == "__main__":
raise SystemExit(main())
Comandos de compilación/grabación/ejecución
Este es un proyecto Python para Raspberry Pi, por lo que no hay un paso de grabación de firmware. En su lugar, crea los archivos, valida la sintaxis y ejecuta.
1) Instalar soporte de paquetes y comprobar importaciones
cd ~/nfc-door-access-alarm
python3 --version
python3 -c "import sys, csv, json, argparse, pathlib; print('standard-library-import-check: OK')"
2) Guardar los archivos
Crea la aplicación principal:
nano access_controller.py
Crea la lista de permitidos:
nano allowlist.json
Crea el script de validación:
nano test_access_controller.py
3) Validar sintaxis de Python
python3 -m py_compile access_controller.py test_access_controller.py
Si tiene éxito, este comando no imprime nada.
4) Ejecutar la validación dry-run en Raspberry Pi o en cualquier ordenador normal
python3 test_access_controller.py
La salida esperada incluirá líneas similares a:
2026-... ACCESS GRANTED uid=04A1B2C3D4
[ALARM] state=ON
2026-... ACCESS DENIED uid=DEADBEEF01
Dry-run validation passed.
5) Ejecutar manualmente el prototipo principal en modo mock
python3 access_controller.py --mock --allowlist allowlist.json --log access_log.csv --mock-sequence 04A1B2C3D4,1122334455,04A1B2C3D4 --max-reads 3
6) Inspeccionar los registros de acceso generados
cat access_log.csv
Estructura esperada:
timestamp,uid,authorized,source,alarm_active
2026-...,04A1B2C3D4,1,nfc,0
2026-...,1122334455,0,nfc,1
2026-...,04A1B2C3D4,1,nfc,0
Validación paso a paso
Esta sección valida el proyecto en torno al objetivo real: acceso controlado por NFC con alarma y registro con marca de tiempo.
1) Confirmar sistema operativo y versión de Python
Ejecuta:
uname -a
python3 --version
Debes tener:
– Raspberry Pi OS Bookworm 64-bit
– Python 3.11.x
2) Confirmar que los buses necesarios están habilitados
Ejecuta:
ls /dev/spidev*
ls /dev/i2c-*
Criterios de éxito:
– Existe el dispositivo SPI para planificar la ruta del PN532
– Existe el dispositivo I2C para planificar la ruta del DS3231
3) Confirmar visibilidad del RTC en I2C
Ejecuta:
sudo i2cdetect -y 1
Criterios de éxito:
– Aparece un dispositivo en 68 u otra dirección RTC esperada según tu hardware
Lo que esto demuestra:
– El DS3231 es eléctricamente accesible en el bus I2C
Lo que aún no demuestra:
– Que la aplicación esté leyendo activamente la hora RTC desde un controlador de hardware dedicado en esta versión del tutorial
4) Confirmar validez sintáctica del código
Ejecuta:
python3 -m py_compile access_controller.py test_access_controller.py
Criterios de éxito:
– No se informan errores
Esto demuestra:
– Los archivos Python son sintácticamente válidos
No demuestra:
– Éxito real de transacción con el PN532
5) Validar la lógica de decisión de acceso en modo mock
Ejecuta:
python3 access_controller.py --mock --allowlist allowlist.json --log access_log.csv --mock-sequence 04A1B2C3D4,CAFEBABE00 --max-reads 2
Criterios de éxito:
– El primer evento imprime ACCESS GRANTED
– El segundo evento imprime ACCESS DENIED
– La consola muestra que la alarma cambia a ON para acceso no autorizado
– Se crea access_log.csv
6) Validar la estructura del registro
Ejecuta:
cat access_log.csv
Comprueba que haya:
– Fila de cabecera
– Exactamente dos filas de eventos
– Evento autorizado marcado como 1
– Evento no autorizado marcado como 0
– El evento no autorizado tiene alarm_active igual a 1
7) Ejecutar el validador automático dry-run incluido
Ejecuta:
python3 test_access_controller.py
Criterios de éxito:
– Línea final: Dry-run validation passed.
8) Siguiente paso con hardware real
En un aula, la siguiente ampliación práctica es sustituir MockPN532Reader por un adaptador SPI real para PN532 y sustituir MockDS3231RTC por un lector DS3231. La lógica central, el registro de eventos y el comportamiento de alarma permanecen iguales, así que validas el hardware por capas en lugar de depurar todo a la vez.
Solución de problemas
La Pi no muestra /dev/spidev0.0
- Vuelve a ejecutar
sudo raspi-config - Habilita SPI otra vez
- Reinicia
- Comprueba si otro overlay o configuración deshabilitó SPI
i2cdetect no muestra la dirección 68
- Vuelve a comprobar el cableado del DS3231:
- SDA a GPIO2
- SCL a GPIO3
- GND común
- Alimentación de 3.3 V
- Algunos módulos están etiquetados para 5 V pero aun así pueden exponer líneas I2C incorrectamente para uso directo con la Pi; confirma la compatibilidad lógica de tu módulo
- Verifica que I2C esté habilitado
py_compile informa un error de sintaxis
- Abre de nuevo el archivo y busca:
- Comillas faltantes
- Indentación rota
- Saltos de línea accidentales por copiar/pegar
- Guarda otra vez y vuelve a ejecutar:
python3 -m py_compile access_controller.py test_access_controller.py
La prueba dry-run no crea un archivo de registro
- Confirma que estás en la carpeta correcta
- Comprueba permisos de archivo:
pwd
ls -l
- Asegúrate de que
allowlist.jsonexista y contenga JSON válido
Todas las etiquetas son denegadas
- Asegúrate de que el UID en
allowlist.jsoncoincida exactamente con el formato esperado de la etiqueta - El código normaliza a mayúsculas sin espacios, así que guarda los UID en mayúsculas por claridad
El estado de alarma nunca cambia
- En este tutorial, la alarma es un estado de software impreso en la consola
- Si más adelante añades un zumbador o salida GPIO, confirma que tu código de salida de hardware realmente llama a
set_alarm(True)yset_alarm(False)
Mejoras
Una vez que el prototipo básico funcione, puedes evolucionarlo hacia una unidad de acceso más realista.
Mejoras de software
- Añadir integración con controlador SPI real para PN532
- Encapsula el código específico de hardware dentro de un adaptador
PN532SPIReader - Mantén la misma interfaz
poll_uid() - Añadir acceso real a registros del DS3231
- Implementa un adaptador
DS3231RTCusando lecturas I2C - Usa conversión BCD y devuelve marcas de tiempo ISO
- Guardar nombres de usuario
- Amplía
allowlist.jsonpara mapear UID a nombre del propietario - Tiempo de espera de alarma
- En lugar de limpiar la alarma inmediatamente en la siguiente lectura válida, mantenla activa durante un número configurable de segundos
- Registro de manipulación
- Cuenta intentos repetidos de tarjetas fallidas y eleva una alerta más fuerte tras tres denegaciones
- Salida de liberación de puerta
- Añade un módulo de relé accionado por transistor para un simulador de cerradura de baja tensión
- Página web simple de estado
- Sirve el estado de acceso más reciente y los registros recientes desde una interfaz web solo local
Mejoras del prototipo físico
- Coloca la Pi, el RTC y el lector en una pequeña carcasa
- Monta el lector NFC cerca del marco de una puerta o armario
- Añade un LED de estado claramente etiquetado:
- Verde para acceso concedido
- Rojo para acceso denegado
- Añade un zumbador de baja potencia para indicar acceso denegado
- Usa un HAT UPS o una fuente de alimentación limpia para mejorar la fiabilidad del registro
Lista de verificación final
Usa esta lista antes de declarar el proyecto como completado:
- [ ] Raspberry Pi OS Bookworm 64-bit está instalado en la Raspberry Pi 4 Model B
- [ ] La versión de Python es 3.11.x
- [ ] SPI está habilitado
- [ ] I2C está habilitado
- [ ] El HAT NFC PN532 está configurado en modo SPI
- [ ] El RTC DS3231 está conectado por I2C
- [ ] Existe la carpeta del proyecto
~/nfc-door-access-alarm - [ ]
access_controller.pyestá guardado - [ ]
allowlist.jsonestá guardado - [ ]
test_access_controller.pyestá guardado - [ ]
python3 -m py_compile access_controller.py test_access_controller.pyse ejecuta sin errores - [ ]
python3 test_access_controller.pyimprimeDry-run validation passed. - [ ] La ejecución manual en modo mock muestra un evento concedido y uno denegado
- [ ]
access_log.csvcontiene registros con marca de tiempo - [ ] Entiendes que este es un prototipo educativo, no un producto de seguridad certificado
Con este prototipo, tienes una base práctica para un registrador de acceso NFC real y un controlador de alarma de puerta usando la Raspberry Pi 4 Model B + HAT NFC PN532 + RTC DS3231.
Encuentra este producto y/o libros sobre este tema en Amazon
Como afiliado de Amazon, gano con las compras que cumplan los requisitos. Si compras a través de este enlace, ayudas a mantener este proyecto.




