Caso práctico: Acceso NFC por WiFi con Arduino Nano 33 IoT

Caso práctico: Acceso NFC por WiFi con Arduino Nano 33 IoT — hero

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

Ir a 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.

Quiz rápido

Pregunta 1: ¿Cuál es el sistema operativo recomendado para el entorno de prueba?




Pregunta 2: ¿Qué versión de Python es requerida por PlatformIO?




Pregunta 3: ¿Qué herramienta se utiliza exclusivamente para la compilación y el flasheo?




Pregunta 4: ¿Cuál es la placa específica mencionada para usar con PlatformIO?




Pregunta 5: ¿Qué tipo de módulo NFC se requiere en el proyecto?




Pregunta 6: ¿Qué dirección I2C es habitual para la pantalla OLED SSD1306?




Pregunta 7: ¿Qué tipo de tarjetas se utilizan en el proyecto?




Pregunta 8: ¿Qué voltaje lógico maneja el Arduino Nano 33 IoT?




Pregunta 9: ¿Qué se debe hacer si los módulos no tienen resistencias pull-up I2C integradas?




Pregunta 10: ¿Qué tipo de conexión USB se requiere para el Arduino Nano 33 IoT?




Carlos Núñez Zorrilla
Carlos Núñez Zorrilla
Electronics & Computer Engineer

Ingeniero Superior en Electrónica de Telecomunicaciones e Ingeniero en Informática (titulaciones oficiales en España).

Sígueme:
error: Contenido Protegido / Content is protected !!
Scroll to Top