You dont have javascript enabled! Please enable it!

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

ElementoPin en Arduino Nano 33 IoTPin en PN532Pin en OLED SSD1306
Alimentación3V3VCC (3V3)VCC (3V3)
GNDGNDGNDGND
I2C SDASDA (marcado “SDA”/A4)SDASDA
I2C SCLSCL (marcado “SCL”/A5)SCLSCL
IRQ PN532D2IRQ
RESET PN532D3RST 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:
Scroll al inicio