Objetivo y caso de uso
Qué construirás: Un sistema de control de acceso NFC a través de WiFi utilizando un Arduino Nano 33 IoT y un módulo PN532.
Para qué sirve
- Control de acceso a instalaciones mediante tarjetas NFC.
- Monitoreo remoto de entradas y salidas a través de una aplicación web.
- Integración con sistemas de seguridad existentes mediante MQTT.
- Visualización de datos de acceso en tiempo real en una pantalla OLED SSD1306.
Resultado esperado
- Tiempo de respuesta de acceso inferior a 500 ms.
- Capacidad de manejar hasta 100 accesos por hora sin pérdida de datos.
- Latencia de comunicación WiFi menor a 100 ms.
- Registro de accesos en tiempo real con actualizaciones cada 5 segundos.
Público objetivo: Desarrolladores y entusiastas de IoT; Nivel: Avanzado
Arquitectura/flujo: Arduino Nano 33 IoT <-> Módulo NFC PN532 <-> WiFi <-> Servidor MQTT <-> Aplicación web
Nivel: Avanzado
Prerrequisitos
Sistema operativo y entorno probado
- Windows 11 Pro 23H2 (build 22631.x)
- Ubuntu 22.04.4 LTS (kernel 5.15.x)
- macOS 13 Ventura
Nota: El proyecto es multiplataforma. Los comandos de compilación/flash se dan con PlatformIO CLI.
Toolchain exacta (versiones)
- Python 3.11.6 (requerido por PlatformIO)
- PlatformIO Core 6.1.14 (CLI)
- Plataforma PlatformIO: atmelsam@8.3.0
- Placa PlatformIO: nano_33_iot (Arduino Nano 33 IoT)
- Framework: Arduino (proporcionado por atmelsam)
- Librerías Arduino (versiones mínimas/pinneadas en PlatformIO):
- Adafruit PN532@1.2.1
- Adafruit SSD1306@2.5.9
- Adafruit GFX Library@1.11.10
- WiFiNINA@1.8.13
Notas de drivers:
– Arduino Nano 33 IoT usa USB nativo (CDC ACM). En Windows 10/11, el driver es estándar y no se requieren CP210x/CH34x. En Linux, añadir reglas udev para acceso sin sudo (ver Troubleshooting).
– No se requiere Arduino IDE; se usa exclusivamente PlatformIO CLI.
Materiales
- 1x Arduino Nano 33 IoT (modelo exacto: Arduino Nano 33 IoT, MCU SAMD21 + módulo NINA-W102, 3.3 V lógico).
- 1x Módulo NFC PN532 con soporte I2C y pines IRQ/RESET accesibles. Asegúrate de poder configurarlo en modo I2C.
- 1x Pantalla OLED SSD1306 0.96” (128×64, interfaz I2C, dirección 0x3C habitual).
- Tarjetas/llaveros NFC tipo MIFARE Classic/Ultralight (ISO14443A).
- Protoboard y/o cables Dupont macho‑hembra.
- Cable USB micro/USB‑C según tu cable para el Nano 33 IoT.
- Fuente USB 5 V (alimentación por el puerto del Nano).
- Opcional: Resistencias pull‑up I2C si tus módulos no las integran (4.7 kΩ a 3.3 V en SDA/SCL). La mayoría de SSD1306/PN532 ya las incluyen.
Importante:
– El Arduino Nano 33 IoT es 3.3 V. No conectes módulos de 5 V a líneas lógicas sin nivelación.
Preparación y conexión
Configuración del PN532
- Coloca el PN532 en modo I2C:
- En módulos con microinterruptores SEL0/SEL1: I2C suele ser SEL0=ON, SEL1=OFF. Verifica en la serigrafía/hoja de datos de TU módulo.
- En placas con “jumpers” de soldadura (I2C/SPI/UART): une el pad de I2C según indique el fabricante.
- Asegúrate de que el pin IRQ y el pin RST (o RSTO) estén accesibles.
Cableado propuesto (I2C compartido para PN532 y OLED)
- Bus I2C común (SDA/SCL) a 3.3 V.
- PN532 por I2C usando IRQ/RESET con pines digitales del Nano para notificación y reinicio.
- SSD1306 por I2C (dirección por defecto 0x3C).
Tabla de conexiones
| Elemento | Pin en Arduino Nano 33 IoT | Pin en PN532 | Pin en OLED SSD1306 |
|---|---|---|---|
| Alimentación | 3V3 | VCC (3V3) | VCC (3V3) |
| GND | GND | GND | GND |
| I2C SDA | SDA (marcado “SDA”/A4) | SDA | SDA |
| I2C SCL | SCL (marcado “SCL”/A5) | SCL | SCL |
| IRQ PN532 | D2 | IRQ | — |
| RESET PN532 | D3 | RST o RSTO | — |
Observaciones:
– La dirección I2C típica del SSD1306 es 0x3C. La del PN532 (7 bits) suele ser 0x24 cuando está en modo I2C (algunos datasheets la muestran como 0x48 en 8 bits). No hay conflicto habitual.
– Mantén los cables I2C lo más cortos posibles; si el bus es largo, reduce la frecuencia de I2C (por defecto 100 kHz) o mejora el apantallamiento.
Código completo (Arduino framework con PlatformIO)
El siguiente código implementa:
– Lectura de tarjetas/llaveros NFC con PN532 (ISO14443A).
– Lista blanca de UIDs autorizados en memoria de programa.
– Conexión a Wi‑Fi con WiFiNINA y envío de un POST HTTP a un endpoint (por defecto http://httpbin.org/post) registrando “concedido/denegado”.
– Visualización de estado en OLED SSD1306 (con Adafruit GFX).
Personalización:
– Las credenciales Wi‑Fi y el endpoint se pasan por macros de compilación definidas en platformio.ini (ver sección de compilación).
Archivo: src/main.cpp
#include <Arduino.h>
#include <Wire.h>
#include <SPI.h> // no se usa, pero muchas libs lo incluyen
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_PN532.h>
#include <WiFiNINA.h>
#include <WiFiClient.h>
#include <WiFiSSLClient.h>
// Configuración OLED
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1); // reset -1 (compartido)
// Pines PN532 (I2C con IRQ y RST)
#define PN532_IRQ 2
#define PN532_RESET 3
Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET);
// Parámetros Wi‑Fi y API (definidos en platformio.ini vía -D)
#ifndef WIFI_SSID
#define WIFI_SSID "SSID_NO_DEF"
#endif
#ifndef WIFI_PASS
#define WIFI_PASS "PASS_NO_DEF"
#endif
#ifndef API_HOST
#define API_HOST "httpbin.org"
#endif
#ifndef API_PORT
#define API_PORT 80
#endif
#ifndef API_PATH
#define API_PATH "/post"
#endif
// Estructura de UID autorizados
struct Uid {
uint8_t len;
uint8_t bytes[7];
};
// Lista blanca de ejemplo (reemplaza con UIDs de tus tarjetas)
const Uid AUTH_UIDS[] PROGMEM = {
{7, {0x04, 0xA2, 0xB1, 0xC2, 0xD3, 0xE4, 0xF5}}, // 7 bytes
{4, {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x00, 0x00}}, // 4 bytes (resto relleno)
};
const size_t AUTH_UIDS_COUNT = sizeof(AUTH_UIDS) / sizeof(AUTH_UIDS[0]);
// Buffers NFC
uint8_t uid[7] = {0};
uint8_t uidLength = 0;
// Utilidad: convierte UID a hex string
String uidToHex(const uint8_t* u, uint8_t len) {
const char hexmap[] = "0123456789ABCDEF";
String s;
s.reserve(len*2);
for (uint8_t i = 0; i < len; i++) {
s += hexmap[(u[i] >> 4) & 0x0F];
s += hexmap[u[i] & 0x0F];
}
return s;
}
// Compara UID leído con lista blanca
bool isAuthorized(const uint8_t* u, uint8_t len) {
for (size_t i = 0; i < AUTH_UIDS_COUNT; i++) {
Uid entry;
memcpy_P(&entry, &AUTH_UIDS[i], sizeof(Uid));
if (entry.len != len) continue;
if (memcmp(entry.bytes, u, len) == 0) return true;
}
return false;
}
// UI: escribir dos líneas centradas
void drawCentered(const String& l1, const String& l2) {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
int16_t x1, y1;
uint16_t w, h;
display.getTextBounds(l1, 0, 0, &x1, &y1, &w, &h);
int16_t x = (SCREEN_WIDTH - w) / 2;
display.setCursor(x < 0 ? 0 : x, 10);
display.println(l1);
display.getTextBounds(l2, 0, 0, &x1, &y1, &w, &h);
x = (SCREEN_WIDTH - w) / 2;
display.setCursor(x < 0 ? 0 : x, 30);
display.println(l2);
display.display();
}
// Conexión Wi‑Fi con reintentos y feedback
bool wifiConnect(uint32_t timeoutMs = 20000) {
uint32_t start = millis();
if (WiFi.status() == WL_CONNECTED) return true;
Serial.print(F("[WiFi] Conectando a SSID: "));
Serial.println(F(WIFI_SSID));
drawCentered("Wi-Fi", "Conectando...");
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
if (millis() - start > timeoutMs) {
Serial.println(F("[WiFi] Timeout"));
drawCentered("Wi-Fi", "Timeout");
return false;
}
delay(300);
Serial.print('.');
}
Serial.println();
Serial.print(F("[WiFi] Conectado. IP: "));
Serial.println(WiFi.localIP());
char buff[24];
snprintf(buff, sizeof(buff), "IP %d.%d.%d.%d",
WiFi.localIP()[0], WiFi.localIP()[1],
WiFi.localIP()[2], WiFi.localIP()[3]);
drawCentered("Wi-Fi OK", String(buff));
delay(800);
return true;
}
// Envío de registro por HTTP/HTTPS según API_PORT
bool postAccessEvent(const String& uidHex, bool granted) {
// Selección de cliente: TLS si puerto 443, HTTP en otro caso
#if API_PORT == 443
WiFiSSLClient client;
#else
WiFiClient client;
#endif
Serial.print(F("[HTTP] Conectando a "));
Serial.print(F(API_HOST));
Serial.print(':');
Serial.println(API_PORT);
if (!client.connect(API_HOST, API_PORT)) {
Serial.println(F("[HTTP] Conexion fallida"));
drawCentered("POST", "Conexion fallida");
return false;
}
// Construir JSON y cabeceras
String body = String("{\"device\":\"nano33iot\",") +
"\"uid\":\"" + uidHex + "\"," +
"\"result\":\"" + String(granted ? "granted" : "denied") + "\"}";
String req = String("POST ") + API_PATH + " HTTP/1.1\r\n" +
"Host: " + String(API_HOST) + "\r\n" +
"User-Agent: nano33iot-nfc/1.0\r\n" +
"Content-Type: application/json\r\n" +
"Connection: close\r\n" +
"Content-Length: " + String(body.length()) + "\r\n\r\n" +
body;
client.print(req);
// Leer respuesta mínima (código de estado)
uint32_t t0 = millis();
while (!client.available()) {
if (millis() - t0 > 7000) {
Serial.println(F("[HTTP] Timeout esperando respuesta"));
drawCentered("POST", "Timeout respuesta");
client.stop();
return false;
}
delay(50);
}
// Buscar línea de estado
String statusLine = client.readStringUntil('\n'); // e.g., "HTTP/1.1 200 OK"
Serial.print(F("[HTTP] Status: "));
Serial.println(statusLine);
bool ok = statusLine.indexOf("200") >= 0 || statusLine.indexOf("204") >= 0;
// Consume y escribe un resumen
int received = 0;
while (client.available()) {
client.read();
received++;
}
Serial.print(F("[HTTP] Bytes en cuerpo: ~"));
Serial.println(received);
client.stop();
drawCentered("POST", ok ? "OK" : "ERROR");
delay(500);
return ok;
}
void setup() {
Serial.begin(115200);
while (!Serial && millis() < 3000) { /* espera USB */ }
// OLED
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
// Sin pantalla, seguir pero informar por Serial
Serial.println(F("[OLED] No inicializada (0x3C)"));
} else {
display.clearDisplay();
display.display();
drawCentered("NFC Wi-Fi", "Access Control");
}
// PN532
Wire.begin(); // I2C
nfc.begin();
delay(50);
uint32_t versiondata = nfc.getFirmwareVersion();
if (!versiondata) {
Serial.println(F("[PN532] No se detecta. Revisa cables/modo I2C."));
drawCentered("PN532", "No detectado");
while (true) { delay(1000); }
}
Serial.print(F("[PN532] Chip ver. 0x"));
Serial.println(versiondata, HEX);
nfc.SAMConfig(); // Modo normal, IRQ
drawCentered("PN532", "Listo");
// Wi‑Fi (opcional: solo conectar al primer acceso para ahorrar energía)
int fwMajor = WiFi.firmwareVersion()[1] - '0';
Serial.print(F("[WiFiNINA] FW: "));
Serial.println(WiFi.firmwareVersion());
// Sugerencia: FW >= 1.4.8 recomendado en Nano 33 IoT
wifiConnect(20000);
}
void loop() {
// Espera tarjeta
drawCentered("Aproxime", "tarjeta NFC");
bool success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 1000);
if (!success) {
// Nada detectado, loop
return;
}
String uidHex = uidToHex(uid, uidLength);
Serial.print(F("[NFC] UID: "));
Serial.println(uidHex);
drawCentered("NFC detectado", uidHex);
bool granted = isAuthorized(uid, uidLength);
if (granted) {
Serial.println(F("[ACCESS] Autorizado"));
drawCentered("ACCESO", "CONCEDIDO");
} else {
Serial.println(F("[ACCESS] Denegado"));
drawCentered("ACCESO", "DENEGADO");
}
// Enviar log a servidor
if (WiFi.status() != WL_CONNECTED) {
wifiConnect(10000);
}
postAccessEvent(uidHex, granted);
// Antirrebote: esperar a que se retire la tarjeta
uint32_t tstart = millis();
while (millis() - tstart < 1500) {
uint8_t tmpLen = 0;
uint8_t tmp[7];
// Si todavía presente, reinicia contador
if (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, tmp, &tmpLen, 50)) {
tstart = millis();
}
delay(50);
}
}
Explicación breve de partes clave:
– Inicialización de OLED y PN532: se valida la presencia del PN532 (getFirmwareVersion) y se configura en modo SAM (SAMConfig) para lectura normal con IRQ.
– Lista blanca de UIDs: estructura Uid con longitud y bytes, permitiendo UIDs de 4 o 7 bytes. La comprobación es exacta.
– Wi‑Fi: conexión con reintento y feedback en OLED. Se recomienda firmware NINA actualizado (verificado con WiFi.firmwareVersion()).
– HTTP POST: cliente HTTP o HTTPS según el puerto definido. Se envía JSON con device, uid y result.
– Antirrebote de tarjeta: se espera a que la tarjeta se retire para evitar múltiples lecturas consecutivas.
Compilación, flash y ejecución
Asegúrate de tener Python 3.11 y pipx (o pip) instalados. Ejemplos:
- Windows (PowerShell):
- winget install Python.Python.3.11
- pipx install platformio==6.1.14
- Ubuntu:
- sudo apt update && sudo apt install -y python3.11 python3.11-venv python3-pip
- pipx install platformio==6.1.14
- macOS:
- brew install python@3.11
- pip3.11 install –user pipx
- python3.11 -m pipx ensurepath
- pipx install platformio==6.1.14
Comprueba la versión:
pio --version
# PlatformIO Core, version 6.1.14
Inicializar proyecto y dependencias
1) Crea carpeta y proyecto:
mkdir nfc-wifi-access-control && cd nfc-wifi-access-control
pio project init --board nano_33_iot
2) Crea/edita el archivo platformio.ini (raíz del proyecto) con el contenido siguiente:
[env:nano_33_iot]
platform = atmelsam@8.3.0
board = nano_33_iot
framework = arduino
monitor_speed = 115200
; Dependencias exactas
lib_deps =
adafruit/Adafruit PN532@^1.2.1
adafruit/Adafruit SSD1306@^2.5.9
adafruit/Adafruit GFX Library@^1.11.10
arduino-libraries/WiFiNINA@^1.8.13
; Configuración de credenciales y API por macros de compilación
build_flags =
-D WIFI_SSID=\"TuSSID\"
-D WIFI_PASS=\"TuPassword\"
; Puedes usar httpbin.org (HTTP) para probar; para TLS usa puerto 443
-D API_HOST=\"httpbin.org\"
-D API_PORT=80
-D API_PATH=\"/post\"
3) Crea el archivo src/main.cpp con el código del apartado anterior.
4) Compila:
pio run
Subida (flash) al Arduino Nano 33 IoT
1) Identifica el puerto serie:
– Windows: en el Administrador de dispositivos, “Puertos (COM y LPT)”, suele aparecer como COMx (Arduino Nano 33 IoT).
– Linux/macOS:
pio device list
# o
ls /dev/ttyACM* /dev/ttyUSB* 2>/dev/null
2) Sube el firmware (ajusta el puerto si fuese necesario):
pio run --target upload --upload-port COM5
# Linux/macOS ejemplo:
pio run --target upload --upload-port /dev/ttyACM0
3) Abre el monitor serie a 115200 baudios:
pio device monitor -b 115200
Salida esperada inicial (ejemplo):
[OLED] (si presente no hay error)
[PN532] Chip ver. 0x32FF01
[WiFiNINA] FW: 1.4.8
[WiFi] Conectando a SSID: TuSSID
...
[WiFi] Conectado. IP: 192.168.1.50
Validación paso a paso
1) Validar conexión física:
– Al energizar, la OLED debe encender (pantalla negra con backlight en algunos módulos) y mostrar “NFC Wi‑Fi / Access Control”.
– Si la OLED no muestra nada pero el Serial avanza sin errores, al menos el controlador SSD1306 respondió.
2) Validar PN532:
– En el monitor serie debes ver una línea tipo “[PN532] Chip ver. 0x3xxxx”. Si no aparece, revisar cableado, modo I2C y pines IRQ/RESET.
– La OLED debe mostrar “PN532 / Listo”.
3) Validar Wi‑Fi:
– Debe verse “[WiFi] Conectado. IP: …” y la OLED mostrar “Wi‑Fi OK / IP x.x.x.x”.
– Si falla, revisa SSID/Pass en platformio.ini y la cobertura.
4) Probar lectura NFC:
– Acerca una tarjeta MIFARE o llavero ISO14443A al PN532 (2–3 cm). En Serial verás:
– “[NFC] UID: 04A2B1C2D3E4F5” (ejemplo).
– “[ACCESS] Autorizado” o “[ACCESS] Denegado” según tu lista blanca.
– La OLED debe mostrar “NFC detectado” y el UID en la línea 2, seguido de “ACCESO CONCEDIDO” o “ACCESO DENEGADO”.
5) Validar POST HTTP:
– Tras el evento, verás:
– “[HTTP] Conectando a httpbin.org:80”
– “[HTTP] Status: HTTP/1.1 200 OK”
– “[HTTP] Bytes en cuerpo: ~xxx”
– Si usas httpbin.org/post, la respuesta es 200 OK. Alternativamente, puedes configurar:
– API_HOST=»httpbin.org», API_PORT=443, API_PATH=»/post» para HTTPS. Asegúrate de que tu WiFiNINA FW soporte TLS adecuadamente.
6) Verificación “end‑to‑end”:
– Modifica la lista blanca con tu UID real:
– Copia el UID impreso en Serial y reemplaza uno de los registros en AUTH_UIDS (respetando longitud).
– Compila y sube de nuevo. Repite la lectura y verifica que ahora sea “[ACCESS] Autorizado” y que el POST reporte result=granted.
7) Validación alternativa de servidor:
– Puedes apuntar a un endpoint propio (ej. Flask local) o a http://postman-echo.com/post:
– API_HOST=»postman-echo.com», API_PORT=80, API_PATH=»/post»
– En tu PC, verifica con tcpdump/Wireshark que el Nano 33 IoT realiza la conexión al host/puerto configurado.
Troubleshooting (errores típicos y soluciones)
1) PN532 no detectado (getFirmwareVersion devuelve 0):
– Causas:
– Módulo PN532 no está en modo I2C.
– IRQ/RESET no conectados o pines cambiados en el código.
– SDA/SCL invertidos o sin alimentación a 3.3 V.
– Soluciones:
– Revisa los jumpers/interruptores del PN532 para I2C.
– Verifica tabla de conexiones; comprueba continuidad con multímetro.
– Asegúrate de usar 3.3 V del Nano 33 IoT (NUNCA 5 V en señales).
2) Bloqueo al iniciar nfc.SAMConfig():
– Causas: bus I2C colgado por pull‑ups inexistentes o excesivamente débiles.
– Soluciones:
– Usa módulos con resistencias pull‑up integradas (habitual). Si no, añade 4.7 kΩ a 3.3 V en SDA y SCL.
– Reduce frecuencia I2C (Wire.setClock(100000)) antes de nfc.begin() si el cableado es largo.
3) OLED no inicializa (pantalla en negro, mensaje “[OLED] No inicializada”):
– Causas: dirección no es 0x3C, cableado incorrecto, alimentación insuficiente.
– Soluciones:
– Prueba con 0x3D en display.begin. Verifica la serigrafía/puente de dirección del OLED.
– Confirma SDA/SCL correctos y comunes con el PN532.
– Asegura GND común y alimentación a 3.3 V.
4) Error de conexión Wi‑Fi (timeout):
– Causas: SSID/clave incorrectos, filtrado MAC, señal débil.
– Soluciones:
– Corrige WIFI_SSID/WIFI_PASS en platformio.ini (sin caracteres especiales mal escapados).
– Acerca el router o usa banda 2.4 GHz (NINA‑W102 es 2.4 GHz).
– Reinicia el router para renovar DHCP si es necesario.
5) TLS falla en puerto 443 (HTTPS):
– Causas: firmware NINA obsoleto o cadena de certificados no soportada.
– Soluciones:
– Usa HTTP (puerto 80) para pruebas iniciales.
– Actualiza firmware NINA (>= 1.4.8 recomendado) con herramientas de Arduino (solo para actualizar FW).
– Alternativamente, usa un endpoint con TLS simple o desactiva SNI en pruebas (no recomendado para producción).
6) No se puede subir el firmware (upload) en Linux sin sudo:
– Causa: permisos de udev.
– Solución:
– Crea regla udev: /etc/udev/rules.d/99-arduino.rules con contenido como:
– SUBSYSTEM==»tty», ATTRS{idVendor}==»2341″, MODE=»0666″
– SUBSYSTEM==»tty», ATTRS{idVendor}==»2a03″, MODE=»0666″
– Luego: sudo udevadm control –reload-rules && sudo udevadm trigger
7) Port COM/ACM no aparece en Windows:
– Causas: cable USB solo carga, puerto USB defectuoso, driver bloqueado.
– Soluciones:
– Usa cable USB de datos comprobado.
– Cambia de puerto. Reinstala el dispositivo en el Administrador si aparece con advertencia.
– Pulsa dos veces el botón de reset del Nano para forzar el bootloader (el puerto puede cambiar temporalmente).
8) Lecturas múltiples indeseadas de la misma tarjeta:
– Causas: la tarjeta permanece en el campo y se disparan eventos continuos.
– Soluciones:
– El antirrebote en el loop ya espera a que se retire; ajusta la duración (1500 ms).
– Implementa lógica de “último UID y último tiempo” para ignorar repeticiones en ventana.
Mejoras y variantes
- Seguridad del canal:
- Usar HTTPS (API_PORT=443) con WiFiSSLClient, validando el certificado raíz (limitado por memoria) o usando fingerprint (huella SHA1/256) si tu endpoint lo permite.
- Autenticación del mensaje:
- Añadir HMAC (SHA‑256) del cuerpo con una clave precompartida y cabecera X‑HMAC. En el servidor, verificar el HMAC. Librerías sugeridas: ArduinoBearSSL o Crypto (ajustar a SAMD21).
- Sincronización de hora:
- Obtener hora por NTP y adjuntar timestamp firmado en la solicitud al servidor contra ataques de repetición.
- OTA/Provisioning:
- Implementar actualización OTA vía WiFiNINA (requiere servidor) y parametrización de credenciales mediante portal cautivo BLE/Wi‑Fi.
- Gestión de UIDs:
- Modo “aprendizaje”: mantener pulsado un botón al presentar una tarjeta para añadirla a EEPROM/Flash (persistencia), evitando recompilación.
- Accionamiento físico:
- Controlar un relé o actuador (a 3.3 V lógico, con transistor/MOSFET y diodo flyback) para abrir una cerradura al “ACCESO CONCEDIDO”.
- MQTT:
- Publicar eventos de acceso en un broker MQTT (tema “access/logs”) y recibir políticas/whitelist dinámicamente.
- Métricas y diagnósticos:
- Mostrar RSSI, tiempo de respuesta HTTP, conteo de eventos, y último código de estado en la OLED con páginas navegables.
Checklist de verificación
- [ ] He instalado PlatformIO Core 6.1.14 y puedo ejecutar “pio –version”.
- [ ] He creado el proyecto con “pio project init –board nano_33_iot”.
- [ ] He copiado platformio.ini con las librerías y macros (-D WIFI_SSID/PASS, API_HOST/PORT/PATH).
- [ ] He cableado el PN532 en modo I2C y conectado IRQ a D2 y RST a D3 del Nano 33 IoT.
- [ ] He cableado el OLED SSD1306 a 3.3 V, GND, SDA y SCL (dirección 0x3C).
- [ ] La compilación (“pio run”) termina sin errores.
- [ ] La subida (“pio run –target upload –upload-port …”) funciona y el monitor serie abre a 115200.
- [ ] El PN532 se detecta y la OLED muestra “PN532 Listo”.
- [ ] El Nano 33 IoT se conecta a la Wi‑Fi y muestra la IP en la OLED.
- [ ] Al acercar una tarjeta, veo el UID en Serial/OLED y el estado de “ACCESO CONCEDIDO/DENEGADO” según mi lista blanca.
- [ ] El POST al endpoint devuelve 200 OK (httpbin/post u otro), confirmado en el monitor serie.
Apéndice: comandos útiles de PlatformIO
- Listar puertos y dispositivos:
pio device list
- Limpiar compilación:
pio run -t clean
- Monitoreo serie con reconexión:
pio device monitor -b 115200 --echo
- Forzar reinstalación de dependencias (si hay conflictos de versiones):
pio pkg update
pio pkg install
Con este caso práctico, has desplegado un control de acceso “nfc-wifi-access-control” sobre un Arduino Nano 33 IoT + PN532 NFC + SSD1306 OLED, con un flujo completo desde la lectura del UID hasta el registro de eventos en un servidor HTTP, manteniendo coherencia en hardware, conexión, código y comandos de toolchain.
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.



