Caso práctico: Monitor de humedad del suelo con ESP32

Caso práctico: Monitor de humedad del suelo con ESP32 — hero

Objetivo y caso de uso

Qué construirás: Un monitor continuo de hidratación de plantas que mide la humedad del suelo utilizando un sensor capacitivo analógico, activando alertas visuales y sonoras locales cuando se requiere riego.

Por qué es importante / Casos de uso

  • Prototipado agrícola: Sirve como un nodo fundamental de recolección de datos para sistemas automatizados de riego en invernaderos, asegurando que las plantas reciban agua basándose en métricas objetivas en lugar de horarios.
  • Longevidad del hardware: Demuestra la ventaja práctica de la detección capacitiva sobre los sensores resistivos tradicionales, eliminando la rápida corrosión galvánica y la contaminación del suelo.
  • Prevención del estrés de las plantas: Reemplaza las conjeturas con mediciones dieléctricas del suelo empíricas y en tiempo real para prevenir tanto el exceso de riego (pudrición de la raíz) como la falta de agua.
  • Procesamiento de señales: Proporciona una aplicación tangible para mapear valores brutos y no lineales del convertidor analógico a digital (ADC) de 12-bit en métricas de humedad intuitivas del 0–100%.

Resultado esperado

  • Salida serie continua en tiempo real que muestra tanto los valores brutos del ADC de 12-bit (0–4095) como los porcentajes de humedad calculados (0–100%) a una tasa de sondeo de ~1Hz.
  • Alerta visual inmediata (iluminación del LED de estado) y señales sonoras activadas en milisegundos cuando el nivel de humedad cae por debajo del umbral definido.

Audiencia: Aficionados al IoT y desarrolladores de sistemas embebidos; Nivel: Principiante a Intermedio

Arquitectura/flujo: Sensor de humedad capacitivo del suelo → ADC del microcontrolador (lectura analógica de 12-bit) → Mapeo del porcentaje de humedad → Salida GPIO (LED/Zumbador) & Monitor Serie

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. Para este perfil ESP32 DevKitC, el proyecto se comprobó como proyecto PlatformIO: el validador extrajo platformio.ini y src/main.cpp, creó un proyecto temporal y ejecutó pio run contra platform = espressif32, board = esp32dev y framework = arduino. También revisó la estructura del artículo, que los comandos sean copiables con guiones ASCII, y que no aparezcan stacks no soportados como ESP-IDF directo o placas ESP32 no acotadas.

Evidencia de validación publicada

  • Resultado automático: PASS.
  • Estructura parseada: 3 apartados, 2 tablas y 2 bloques de código detectados antes de publicar.
  • Código comprobado: 1 PlatformIO config + 1 ESP32 source/pio run.
  • Catálogo soportado: el texto se contrastó contra los perfiles de dispositivo validables de Prometeo y 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 código publicado, pero no sustituye la prueba física sobre tu placa ESP32 DevKitC exacta, tu cableado, alimentación y entorno WiFi local.

Nota educativa de seguridad

Este proyecto es un prototipo educativo, no un producto certificado. Antes de encender el montaje, verifique la distribución de pines de su revisión exacta de la placa ULX3S, mantenga las señales de E/S de la FPGA a 3.3 V, nunca conecte 5 V directamente a los pines de E/S, desconecte la alimentación antes de cambiar el cableado y use fuentes externas adecuadas para cargas, motores o servos, compartiendo la tierra solo cuando el cableado lo requiera.

Diagrama de bloques conceptual

Vista de alto nivel: qué entra, qué procesa cada bloque y qué sale del sistema.

Arquitectura funcional

Botones ULX3S

Sincronizador/antirrebote

Selector de modo

Generador periodo 20 ms

Comparador ancho de pulso

Salida PWM 50 Hz

Servo SG90

Flujo conceptual de control: entrada de botones, selección de modo, temporización PWM y movimiento del servo.

Ruta de validación

Verilog fuente

Verilator lint/testbench

Yosys síntesis

nextpnr-ecp5

ecppack bitstream

ULX3S programada

La validación automática comprueba sintaxis, simulación/lint y compatibilidad con la toolchain ULX3S/ECP5.

Requisitos previos

Para completar este tutorial con éxito, los estudiantes deben tener:
* Una comprensión básica de la diferencia entre señales analógicas (niveles de voltaje continuos) y señales digitales (estados binarios HIGH/LOW).
* PlatformIO IDE instalado (preferiblemente como extensión de Visual Studio Code) o el PlatformIO Core CLI.
* Familiaridad con técnicas básicas de protoboard, incluyendo el enrutamiento de rieles de alimentación y el uso de resistencias limitadoras de corriente para LEDs.
* Conocimientos básicos de programación en C/C++ (variables, lógica condicional if/else y funciones).

Materiales

Debe usar la siguiente configuración de hardware exacta para asegurarse de que el código y la distribución de pines proporcionados funcionen sin modificaciones:

  • ESP32 DevKitC + sensor de humedad del suelo capacitivo v1.2 + LED de estado + zumbador piezoeléctrico
  • 1x resistencia de 330Ω (Código de colores: Naranja-Naranja-Marrón, utilizada para el LED de estado para prevenir sobrecorriente).
  • 1x Protoboard estándar (se recomiendan 830 puntos de conexión).
  • Cables puente Dupont variados (Macho-Macho y Macho-Hembra dependiendo del conector específico de su sensor).
  • 1x cable Micro-USB o USB-C (asegúrese de que sea un cable de sincronización de datos, no solo de carga).
  • Una taza de tierra seca y una taza de agua (para calibración y pruebas).

Configuración/Conexión

La conexión correcta del hardware es fundamental. El ESP32 funciona con niveles lógicos de 3.3V. El sensor de humedad del suelo capacitivo v1.2 incluye un regulador de voltaje integrado, pero la mejor práctica es alimentarlo desde el pin de 3.3V del ESP32 para asegurar que su salida analógica nunca exceda la clasificación de entrada ADC máxima del ESP32 (3.3V). Suministrar 5V al sensor podría dar como resultado señales analógicas que dañen permanentemente los pines GPIO del ESP32.

Usamos el GPIO 34 para la entrada analógica. El ESP32 tiene dos ADC internos. El ADC2 se comparte con la radio Wi-Fi y no se puede usar de manera confiable cuando el Wi-Fi está activo. El ADC1 (que incluye el GPIO 34) funciona de manera independiente del stack Wi-Fi, lo que lo convierte en la opción estándar para lecturas de sensores robustas.

Tabla de cableado

Componente Pin / Terminal del componente Pin del ESP32 DevKitC Notas
Sensor capacitivo VCC / V+ 3V3 Alimentar con 3.3V para proteger el ADC del ESP32.
Sensor capacitivo GND / G GND Referencia de tierra común.
Sensor capacitivo AOUT / AU GPIO 34 Conectado a ADC1_CH6.
LED de estado Ánodo (Pata larga) GPIO 25 Conectar a través de la resistencia de 330Ω.
LED de estado Cátodo (Pata corta) GND Referencia de tierra común.
Zumbador piezoeléctrico Positivo (+) GPIO 26 Controlado a través del PWM por hardware del ESP32 (LEDC).
Zumbador piezoeléctrico Negativo (-) GND Referencia de tierra común.

Nota de hardware: El zumbador piezoeléctrico puede ser activo o pasivo. El código proporcionado utiliza una señal de Modulación por Ancho de Pulso (PWM), que generará un tono específico en un zumbador pasivo, y también activará con éxito un zumbador activo al alternar su alimentación rápidamente.

Código validado

El proyecto utiliza PlatformIO. Deberá configurar su archivo de entorno y el archivo fuente de su aplicación principal.

platformio.ini

Cree o reemplace el contenido de su archivo platformio.ini con la siguiente configuración:

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200

src/main.cpp

Copie el siguiente código C++ en su archivo src/main.cpp. Este código implementa la lectura analógica continua, realiza una interpolación lineal para calcular el porcentaje de humedad y maneja la lógica para las alertas de hardware.

Vista pública parcial del archivo validado. El código completo se muestra a miembros y en PDF/Print.

#include <Arduino.h>

// --------------------------------------------------------
// Hardware Pin Definitions
// --------------------------------------------------------
const int MOISTURE_PIN = 34; // ADC1 Channel 6
const int LED_PIN      = 25; // Digital Output for Status LED
const int BUZZER_PIN   = 26; // Digital Output for Piezo Buzzer

// --------------------------------------------------------
// PWM Configuration for Piezo Buzzer
// --------------------------------------------------------
const int PWM_FREQ       = 2000; // 2 kHz audio frequency
const int PWM_CHANNEL    = 0;    // ESP32 Hardware Timer Channel 0
const int PWM_RESOLUTION = 8;    // 8-bit resolution (0-255)

// --------------------------------------------------------
// Calibration Constants
// --------------------------------------------------------
// IMPORTANT: Capacitive sensors output a HIGHER voltage when dry 
// and a LOWER voltage when wet. These values represent the 12-bit 
// ADC readings (0-4095). You must calibrate these for your specific sensor.
const int DRY_VALUE = 3200;  // Expected ADC reading in completely dry air
const int WET_VALUE = 1400;  // Expected ADC reading submerged in water

// --------------------------------------------------------
// Application Logic Constants
// --------------------------------------------------------
const int ALARM_THRESHOLD_PERCENT = 30; // Trigger alert at or below 30% moisture

void setup() {
    // Initialize Serial Communication for debugging
    Serial.begin(115200);

    // Configure Digital Output Pins
    pinMode(LED_PIN, OUTPUT);
    digitalWrite(LED_PIN, LOW); // Ensure LED is off at boot

    // Configure LEDC PWM peripheral for the Buzzer
    // This allows us to generate a clean square wave for a passive piezo
    ledcSetup(PWM_CHANNEL, PWM_FREQ, PWM_RESOLUTION);
    ledcAttachPin(BUZZER_PIN, PWM_CHANNEL);
    ledcWrite(PWM_CHANNEL, 0); // Ensure buzzer is silent at boot

    Serial.println("=========================================");
    Serial.println(" ESP32 Soil Moisture Monitor Initialized ");
    Serial.println("=========================================");

    // Allow hardware to stabilize
    delay(1000); 
}
// ... continúa para miembros en el código completo validado ...

🔒 Parte del código validado es premium. Con el pase de 7 días o la suscripción mensual podrás consultar el archivo completo validado.

#include <Arduino.h>

// --------------------------------------------------------
// Hardware Pin Definitions
// --------------------------------------------------------
const int MOISTURE_PIN = 34; // ADC1 Channel 6
const int LED_PIN      = 25; // Digital Output for Status LED
const int BUZZER_PIN   = 26; // Digital Output for Piezo Buzzer

// --------------------------------------------------------
// PWM Configuration for Piezo Buzzer
// --------------------------------------------------------
const int PWM_FREQ       = 2000; // 2 kHz audio frequency
const int PWM_CHANNEL    = 0;    // ESP32 Hardware Timer Channel 0
const int PWM_RESOLUTION = 8;    // 8-bit resolution (0-255)

// --------------------------------------------------------
// Calibration Constants
// --------------------------------------------------------
// IMPORTANT: Capacitive sensors output a HIGHER voltage when dry 
// and a LOWER voltage when wet. These values represent the 12-bit 
// ADC readings (0-4095). You must calibrate these for your specific sensor.
const int DRY_VALUE = 3200;  // Expected ADC reading in completely dry air
const int WET_VALUE = 1400;  // Expected ADC reading submerged in water

// --------------------------------------------------------
// Application Logic Constants
// --------------------------------------------------------
const int ALARM_THRESHOLD_PERCENT = 30; // Trigger alert at or below 30% moisture

void setup() {
    // Initialize Serial Communication for debugging
    Serial.begin(115200);

    // Configure Digital Output Pins
    pinMode(LED_PIN, OUTPUT);
    digitalWrite(LED_PIN, LOW); // Ensure LED is off at boot

    // Configure LEDC PWM peripheral for the Buzzer
    // This allows us to generate a clean square wave for a passive piezo
    ledcSetup(PWM_CHANNEL, PWM_FREQ, PWM_RESOLUTION);
    ledcAttachPin(BUZZER_PIN, PWM_CHANNEL);
    ledcWrite(PWM_CHANNEL, 0); // Ensure buzzer is silent at boot

    Serial.println("=========================================");
    Serial.println(" ESP32 Soil Moisture Monitor Initialized ");
    Serial.println("=========================================");

    // Allow hardware to stabilize
    delay(1000); 
}

void loop() {
    // 1. Read the raw analog voltage from the sensor
    int rawAdcValue = analogRead(MOISTURE_PIN);

    // 2. Map the raw ADC value to a 0-100 percentage scale.
    // We invert the mapping parameters because DRY is high, WET is low.
    int moisturePercent = map(rawAdcValue, DRY_VALUE, WET_VALUE, 0, 100);

    // 3. Constrain the percentage to strictly 0-100.
    // This prevents negative numbers or values > 100% if the sensor 
    // reads slightly outside the hardcoded calibration range.
    moisturePercent = constrain(moisturePercent, 0, 100);

    // 4. Output telemetry to the Serial Monitor
    Serial.print("Raw ADC: ");
    Serial.print(rawAdcValue);
    Serial.print("\t | Moisture: ");
    Serial.print(moisturePercent);
    Serial.println("%");

    // 5. Evaluate the alert threshold
    if (moisturePercent <= ALARM_THRESHOLD_PERCENT) {
        // Condition: Soil is dangerously dry.
        // Action: Flash the LED and pulse the buzzer.

        digitalWrite(LED_PIN, HIGH);
        // Set PWM duty cycle to 50% (128 out of 255) to generate sound
        ledcWrite(PWM_CHANNEL, 128); 
        delay(500); // Wait 500ms

        digitalWrite(LED_PIN, LOW);
        // Set PWM duty cycle to 0% to silence buzzer
        ledcWrite(PWM_CHANNEL, 0);   
        delay(500); // Wait 500ms

    } else {
        // Condition: Soil moisture is adequate.
        // Action: Keep alerts turned off and delay before next reading.

        digitalWrite(LED_PIN, LOW);
        ledcWrite(PWM_CHANNEL, 0);
        delay(1000); // Poll sensor once per second
    }
}

Comandos de compilación/flasheo/ejecución

Para compilar, subir y monitorear el código en su ESP32 DevKitC, use la CLI de PlatformIO. Abra su terminal en el directorio raíz de su proyecto (donde se encuentra platformio.ini) y ejecute los siguientes comandos.

Referencia de comandos

Acción Comando Propósito
Compilar pio run Compila el código fuente en C++ y enlaza el framework de Arduino.
Subir pio run --target upload Flashea el binario del firmware compilado en el ESP32.
Monitorear pio device monitor Abre el monitor serie para ver la salida de telemetría.

Flujo de ejecución

  1. Conecte el ESP32 DevKitC a su computadora a través del cable USB. Asegúrese de que los controladores CP210x o CH34x estén instalados si su sistema operativo no reconoce el dispositivo automáticamente.
  2. Ejecute pio run para verificar que no haya errores de sintaxis y que el entorno esté configurado correctamente.
  3. Ejecute pio run --target upload para flashear el dispositivo. Si la terminal muestra «Connecting…» y se detiene, es posible que deba mantener presionado el botón «BOOT» en el ESP32 DevKitC hasta que comience el proceso de flasheo.
  4. Ejecute pio device monitor para observar la salida serie. Debería ver inmediatamente el banner de inicialización seguido de lecturas continuas del ADC.

Validación paso a paso

Siga estos puntos de control para asegurarse de que su prototipo sea completamente funcional y esté calibrado correctamente.

  1. Comprobación de comunicación serie:
    • Acción: Abra el monitor serie después del flasheo.
    • Observación esperada: La consola imprime «ESP32 Soil Moisture Monitor Initialized» seguido de líneas de datos cada segundo.
    • Condición de aprobación: El texto es legible (sin caracteres extraños), lo que confirma que la tasa de baudios de 115200 es correcta.
  2. Línea base de aire seco (Calibración al 0%):
    • Acción: Sostenga el sensor al aire libre, sin tocar nada.
    • Observación esperada: El valor de Raw ADC se estabiliza (por ejemplo, alrededor de 3100 a 3300).
    • Condición de aprobación: Anote este número. Actualice la constante DRY_VALUE en el código para que coincida con esta lectura y vuelva a flashear el ESP32 si se desvía en más de 100 del valor predeterminado.
  3. Inmersión en agua (Calibración al 100%):
    • Acción: Sumerja el sensor capacitivo en un vaso de agua hasta la línea de seguridad marcada.
    • Observación esperada: El valor de Raw ADC cae significativamente y se estabiliza (por ejemplo, alrededor de 1400).
    • Condición de aprobación: El monitor serie muestra 100%. Actualice la constante WET_VALUE en el código y vuelva a flashear si no alcanza el 100%.
  4. Verificación de la lógica de alerta:
    • Acción: Retire lentamente el sensor del agua y séquelo por completo.
    • Observación esperada: El porcentaje de humedad disminuye. Una vez que alcanza el 30% o menos, el LED parpadea y el zumbador emite un tono de 2000 Hz.
    • Condición de aprobación: Las alertas visuales y sonoras se activan sincrónicamente exactamente cuando la humedad marca <= 30%, verificando que el umbral lógico funciona de manera segura.

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 valor de 'border-radius' aplicado a la clase '.case-objective' y las demás clases principales?




Pregunta 2: ¿Cuál es el valor de 'border-radius' aplicado a la clase '.case-objective' y las demás clases principales?




Pregunta 3: ¿Cuál es el valor de 'border-radius' aplicado a la clase '.case-objective' y las demás clases principales?




Pregunta 4: ¿Cuál es el valor de 'border-radius' aplicado a la clase '.case-objective' y las demás clases principales?




Pregunta 5: ¿Cuál es el valor de 'border-radius' aplicado a la clase '.case-objective' y las demás clases principales?




Pregunta 6: ¿Cuál es el valor de 'border-radius' aplicado a la clase '.case-objective' y las demás clases principales?




Pregunta 7: ¿Cuál es el valor de 'border-radius' aplicado a la clase '.case-objective' y las demás clases principales?




Pregunta 8: ¿Cuál es el valor de 'border-radius' aplicado a la clase '.case-objective' y las demás clases principales?




Pregunta 9: ¿Cuál es el valor de 'border-radius' aplicado a la clase '.case-objective' y las demás clases principales?




Pregunta 10: ¿Cuál es el valor de 'border-radius' aplicado a la clase '.case-objective' y las demás clases principales?




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:


Caso práctico: Monitor de red local con ESP32

Caso práctico: Monitor de red local con ESP32 — hero

Objetivo y caso de uso

Qué construirás: Un Monitor de Red Local para Técnicos de Campo independiente utilizando un ESP32 configurado como Punto de Acceso por Software (SoftAP). Este transmite una red Wi-Fi segura y localizada para servir un panel de diagnóstico en tiempo real y aislado de internet directamente a un teléfono inteligente o tableta.

Por qué es importante / Casos de uso

  • Diagnóstico independiente de la infraestructura: Permite a los técnicos conectarse directamente a los equipos en entornos sin conexión, como campos agrícolas remotos, sótanos industriales profundos o sitios de nueva construcción.
  • Registro seguro de fallas de la máquina: Se interconecta con relés de falla industriales para que el personal pueda monitorear de manera segura los estados de la máquina a través del navegador, evitando la exposición física a paneles de control de alto voltaje.
  • Interfaz hombre-máquina (HMI) aislada: Ofrece un portal de configuración altamente seguro y aislado de internet (air-gapped) al que no se puede acceder desde la internet pública, reduciendo drásticamente las superficies de ataque de ciberseguridad.
  • Monitoreo de control de acceso: Actúa como un monitor localizado y temporal para puertas de racks de servidores o puertas de seguridad, registrando instantáneamente los estados de apertura/cierre.

Resultado esperado

  • El ESP32 transmite de manera confiable una red Wi-Fi protegida por WPA2 (SSID: ESP32-FieldMonitor) con un tiempo de conexión de cliente de <2 segundos.
  • Un panel web totalmente adaptable a dispositivos móviles se carga sin internet externo, renderizando elementos de la interfaz de usuario de diagnóstico.
  • El estado del hardware en tiempo real y los registros de fallas se actualizan en el navegador del cliente con una latencia de red de <50ms.

Audiencia: Desarrolladores de IoT Industrial, Técnicos de Campo, Ingenieros de Mantenimiento; Nivel: Intermedio

Arquitectura/flujo: ESP32 (Modo SoftAP) → Transmite SSID WPA2 → El teléfono inteligente del técnico se conecta → Servidor Web ESP32 → Sirve Panel HTML/JS & Transmite datos GPIO en tiempo real.

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. Para este perfil ESP32 DevKitC, el proyecto se comprobó como proyecto PlatformIO: el validador extrajo platformio.ini y src/main.cpp, creó un proyecto temporal y ejecutó pio run contra platform = espressif32, board = esp32dev y framework = arduino. También revisó la estructura del artículo, que los comandos sean copiables con guiones ASCII, y que no aparezcan stacks no soportados como ESP-IDF directo o placas ESP32 no acotadas.

Evidencia de validación publicada

  • Resultado automático: PASS.
  • Estructura parseada: 4 apartados, 2 tablas y 2 bloques de código detectados antes de publicar.
  • Código comprobado: 1 PlatformIO config + 1 ESP32 source/pio run.
  • Catálogo soportado: el texto se contrastó contra los perfiles de dispositivo validables de Prometeo y 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 código publicado, pero no sustituye la prueba física sobre tu placa ESP32 DevKitC exacta, tu cableado, alimentación y entorno WiFi local.

Nota educativa de seguridad

Este proyecto es un prototipo educativo, no un producto certificado. Antes de encender la configuración, verifica el esquema de pines de la revisión exacta de tu placa ULX3S, mantén las señales de E/S de la FPGA a 3.3 V, nunca conectes 5 V directamente a los pines de E/S, desconecta la alimentación antes de cambiar el cableado y utiliza fuentes externas adecuadas para cargas, motores o servos mientras compartes la tierra (ground) solo cuando el cableado lo requiera.

Diagrama de bloques conceptual

Vista de alto nivel: qué entra, qué procesa cada bloque y qué sale del sistema.

Arquitectura funcional

ESP32 (Modo SoftAP)

Transmite SSID WPA2

El teléfono inteligente del técnico se co…

Servidor Web ESP32

Sirve Panel HTML/JS & Transmite datos GPI…

Flujo conceptual de señales y responsabilidades entre bloques del dispositivo.

Ruta de validación

Código fuente

PlatformIO build

Flash

Monitor serie

Resumen conceptual de las herramientas usadas para comprobar el material publicado.

Requisitos previos

Para completar con éxito este tutorial, necesitarás:
* Conocimiento básico de programación en C++ y conceptos de GPIO de microcontroladores.
* PlatformIO IDE instalado (ya sea como una extensión de Visual Studio Code o a través de la Interfaz de Línea de Comandos).
* Conocimiento básico de conceptos de redes, específicamente Puntos de Acceso Wi-Fi (SSID, WPA2) y direccionamiento IP.
* Un navegador web en un dispositivo con Wi-Fi (teléfono inteligente, tableta o computadora portátil) para ver el panel.


Materiales

  • Microcontrolador: ESP32 DevKitC (Modelo exacto: ESP32 DevKitC V4, típicamente equipado con el módulo ESP32-WROOM-32).
  • Dispositivo de entrada: Pulsador o interruptor de contacto seco (esto simulará un relé de falla de máquina externo o contacto de puerta).
  • Indicador visual: LED de estado estándar de 5mm (ej., azul o verde).
  • Componentes pasivos: 1x resistencia de 330Ω (limitadora de corriente para el LED).
  • Prototipado: Protoboard estándar y cables puente macho a macho.
  • Conexión: Cable micro-USB (debe soportar tanto energía como transferencia de datos; los cables de solo carga fallarán al flashear el dispositivo).

(Nota: Dependiendo de tu fabricante específico de ESP32 DevKitC, es posible que necesites instalar controladores USB a UART CP210x o CH34x en tu sistema operativo para permitir que PlatformIO reconozca el dispositivo).


Configuración/Conexión

La configuración de hardware utiliza las resistencias pull-up internas del ESP32 para la entrada del contacto, minimizando la necesidad de componentes pasivos externos. Cuando el interruptor de contacto está abierto, la resistencia interna lleva el pin a HIGH (ALTO). Cuando el interruptor está cerrado, conecta el pin a Tierra (LOW o BAJO).

Tabla de cableado:

Componente Pin ESP32 DevKitC Conexión / Destino Descripción
Interruptor de contacto GPIO 18 Terminal 1 del interruptor Configurado como INPUT_PULLUP.
Interruptor de contacto GND Terminal 2 del interruptor Lleva el GPIO 18 a LOW cuando está cerrado.
LED de estado GPIO 19 Ánodo del LED (Pata larga) Configurado como OUTPUT.
LED de estado GND Cátodo del LED (Pata corta) a través de resistencia de 330Ω Completa el circuito del LED de forma segura.

Notas importantes de hardware:
1. Asegúrate de usar una resistencia de 330Ω en serie con el LED de estado para evitar extraer corriente excesiva del pin GPIO del ESP32, lo cual podría dañar el microcontrolador.
2. No conectes el interruptor de contacto a ninguna fuente de voltaje externa. Debe actuar como un «contacto seco» (un simple cierre mecánico a Tierra).


Código validado

El proyecto requiere dos archivos dentro de tu espacio de trabajo de PlatformIO. El archivo platformio.ini configura el entorno de compilación, mientras que src/main.cpp contiene la lógica de la aplicación.

Configuración de PlatformIO

Crea o sobrescribe el archivo platformio.ini en la raíz del directorio de tu proyecto con la siguiente configuración. Esto asegura que se utilicen la definición correcta de la placa y la tasa de baudios del monitor serie.

; platformio.ini
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200

Código fuente de la aplicación

Crea o sobrescribe el archivo src/main.cpp con el siguiente código completo y compilable.

Vista pública parcial del archivo validado. El código completo se muestra a miembros y en PDF/Print.

// src/main.cpp
#include <Arduino.h>
#include <WiFi.h>
#include <WebServer.h>

// Hardware Pin Definitions
#define CONTACT_PIN 18
#define STATUS_LED_PIN 19

// SoftAP Network Credentials
const char* ssid = "ESP32-FieldMonitor";
const char* password = "admin1234"; // WPA2 requires a minimum of 8 characters

// Initialize the WebServer on port 80
WebServer server(80);

// Global state variables
int currentContactState = HIGH;
int lastContactState = HIGH;

// HTML Dashboard stored in Program Memory (PROGMEM) to save RAM
const char dashboard_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Field Monitor Dashboard</title>
  <style>
    body {
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
      text-align: center;
      background-color: #e9ecef;
      margin: 0;
      padding: 20px;
    }
    .card {
      background: white;
      max-width: 400px;
      margin: 40px auto;
      padding: 30px;
      border-radius: 12px;
      box-shadow: 0 8px 16px rgba(0,0,0,0.1);
    }
    h2 {
      color: #343a40;
      margin-top: 0;
    }
    .status-box {
      font-size: 1.8em;
      font-weight: bold;
      margin: 20px 0;
      padding: 20px;
      border-radius: 8px;
      transition: background-color 0.3s, color 0.3s;
    }
    .loading { background-color: #f8f9fa; color: #6c757d; border: 2px dashed #6c757d; }
    .closed { background-color: #d4edda; color: #155724; border: 2px solid #28a745; }
    .open { background-color: #f8d7da; color: #721c24; border: 2px solid #dc3545; }
    .footer {
      margin-top: 20px;
      font-size: 0.85em;
      color: #6c757d;
    }
  </style>
</head>
<body>
  <div class="card">
    <h2>Machine Contact Status</h2>
    <div id="contact-state" class="status-box loading">Awaiting Data...</div>
    <div class="footer">Auto-refreshing every 500ms via JSON API</div>
  </div>

  <script>
    // Asynchronous function to fetch status from the ESP32
    function fetchStatus() {
      fetch('/api/status')
        .then(response => {
          if (!response.ok) {
            throw new Error('Network response was not ok');
          }
          return response.json();
        })
        .then(data => {
          const statusDiv = document.getElementById('contact-state');
          // data.contact is 0 when closed (LOW) due to INPUT_PULLUP
          if(data.contact === 0) {
            statusDiv.innerHTML = "CONTACT CLOSED";
            statusDiv.className = "status-box closed";
          } else {
            statusDiv.innerHTML = "CONTACT OPEN";
            statusDiv.className = "status-box open";
          }
// ... continúa para miembros en el código completo validado ...

🔒 Parte del código validado es premium. Con el pase de 7 días o la suscripción mensual podrás consultar el archivo completo validado.

// src/main.cpp
#include <Arduino.h>
#include <WiFi.h>
#include <WebServer.h>

// Hardware Pin Definitions
#define CONTACT_PIN 18
#define STATUS_LED_PIN 19

// SoftAP Network Credentials
const char* ssid = "ESP32-FieldMonitor";
const char* password = "admin1234"; // WPA2 requires a minimum of 8 characters

// Initialize the WebServer on port 80
WebServer server(80);

// Global state variables
int currentContactState = HIGH;
int lastContactState = HIGH;

// HTML Dashboard stored in Program Memory (PROGMEM) to save RAM
const char dashboard_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Field Monitor Dashboard</title>
  <style>
    body {
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
      text-align: center;
      background-color: #e9ecef;
      margin: 0;
      padding: 20px;
    }
    .card {
      background: white;
      max-width: 400px;
      margin: 40px auto;
      padding: 30px;
      border-radius: 12px;
      box-shadow: 0 8px 16px rgba(0,0,0,0.1);
    }
    h2 {
      color: #343a40;
      margin-top: 0;
    }
    .status-box {
      font-size: 1.8em;
      font-weight: bold;
      margin: 20px 0;
      padding: 20px;
      border-radius: 8px;
      transition: background-color 0.3s, color 0.3s;
    }
    .loading { background-color: #f8f9fa; color: #6c757d; border: 2px dashed #6c757d; }
    .closed { background-color: #d4edda; color: #155724; border: 2px solid #28a745; }
    .open { background-color: #f8d7da; color: #721c24; border: 2px solid #dc3545; }
    .footer {
      margin-top: 20px;
      font-size: 0.85em;
      color: #6c757d;
    }
  </style>
</head>
<body>
  <div class="card">
    <h2>Machine Contact Status</h2>
    <div id="contact-state" class="status-box loading">Awaiting Data...</div>
    <div class="footer">Auto-refreshing every 500ms via JSON API</div>
  </div>

  <script>
    // Asynchronous function to fetch status from the ESP32
    function fetchStatus() {
      fetch('/api/status')
        .then(response => {
          if (!response.ok) {
            throw new Error('Network response was not ok');
          }
          return response.json();
        })
        .then(data => {
          const statusDiv = document.getElementById('contact-state');
          // data.contact is 0 when closed (LOW) due to INPUT_PULLUP
          if(data.contact === 0) {
            statusDiv.innerHTML = "CONTACT CLOSED";
            statusDiv.className = "status-box closed";
          } else {
            statusDiv.innerHTML = "CONTACT OPEN";
            statusDiv.className = "status-box open";
          }
        })
        .catch(error => {
          console.error('Error fetching status:', error);
          const statusDiv = document.getElementById('contact-state');
          statusDiv.innerHTML = "CONNECTION LOST";
          statusDiv.className = "status-box loading";
        });
    }

    // Poll the API every 500 milliseconds
    setInterval(fetchStatus, 500);

    // Initial fetch immediately on load
    window.onload = fetchStatus;
  </script>
</body>
</html>
)rawliteral";

// Route Handler: Serve the main HTML dashboard
void handleRoot() {
  server.send(200, "text/html", dashboard_html);
  Serial.println("Dashboard accessed by a client.");
}

// Route Handler: Serve the JSON API for the dashboard to consume
void handleApiStatus() {
  // Construct a simple JSON string manually
  String jsonPayload = "{\"contact\": " + String(currentContactState) + "}";
  server.send(200, "application/json", jsonPayload);
}

// Route Handler: Handle 404 Not Found
void handleNotFound() {
  server.send(404, "text/plain", "404: Not Found");
}

void setup() {
  // Initialize Serial Monitor
  Serial.begin(115200);
  delay(1000); // Allow serial to stabilize
  Serial.println("\n--- ESP32 Field Monitor Initialization ---");

  // Configure Hardware Pins
  pinMode(CONTACT_PIN, INPUT_PULLUP);
  pinMode(STATUS_LED_PIN, OUTPUT);

  // Read initial state
  currentContactState = digitalRead(CONTACT_PIN);
  lastContactState = currentContactState;

  // Set initial LED state (ON when contact is closed/LOW)
  digitalWrite(STATUS_LED_PIN, (currentContactState == LOW) ? HIGH : LOW);

  // Configure Wi-Fi in Access Point (SoftAP) mode
  Serial.print("Configuring Access Point...");
  WiFi.softAP(ssid, password);

  IPAddress IP = WiFi.softAPIP();
  Serial.println(" Ready!");
  Serial.print("SoftAP SSID: ");
  Serial.println(ssid);
  Serial.print("SoftAP IP Address: ");
  Serial.println(IP);

  // Define Web Server Routing
  server.on("/", HTTP_GET, handleRoot);
  server.on("/api/status", HTTP_GET, handleApiStatus);
  server.onNotFound(handleNotFound);

  // Start the Web Server
  server.begin();
  Serial.println("HTTP Web Server started.");
}

void loop() {
  // Handle incoming HTTP client requests
  server.handleClient();

  // Read the physical contact state
  currentContactState = digitalRead(CONTACT_PIN);

  // Detect state changes to update the LED and log to Serial
  if (currentContactState != lastContactState) {
    // Debounce delay (basic implementation)
    delay(50);
    currentContactState = digitalRead(CONTACT_PIN);

    if (currentContactState != lastContactState) {
      if (currentContactState == LOW) {
        Serial.println("EVENT: Contact CLOSED (Active).");
        digitalWrite(STATUS_LED_PIN, HIGH); // Turn LED ON
      } else {
        Serial.println("EVENT: Contact OPEN (Inactive).");
        digitalWrite(STATUS_LED_PIN, LOW);  // Turn LED OFF
      }
      lastContactState = currentContactState;
    }
  }
}


Comandos de compilación/flasheo/ejecución

Usa la CLI de PlatformIO Core para compilar, cargar y monitorear tu proyecto. Abre tu terminal en el directorio raíz del proyecto (donde se encuentra platformio.ini) y ejecuta los siguientes comandos.

Comando Propósito
pio run Compila el proyecto y verifica todas las dependencias y la sintaxis.
pio run --target upload Compila y flashea el firmware compilado al ESP32 DevKitC.
pio device monitor Abre el monitor serie para ver los registros de ejecución a 115200 baudios.

Flujo de trabajo de ejecución:
1. Conecta el ESP32 DevKitC a tu computadora a través del cable micro-USB.
2. Ejecuta pio run para asegurarte de que el código se compila sin errores de sintaxis.
3. Ejecuta pio run --target upload. (Si la carga falla al conectar, mantén presionado el botón BOOT en el ESP32 DevKitC cuando veas «Connecting…» en la terminal).
4. Ejecuta pio device monitor para observar la secuencia de inicialización y verificar la dirección IP del SoftAP.


Validación paso a paso

Sigue estos puntos de control para asegurarte de que el prototipo funcione exactamente como está previsto.

  1. Verificar la inicialización del puerto serie
    • Acción: Observa la salida de la terminal inmediatamente después de ejecutar pio device monitor o presionar el botón EN (Reinicio) en el ESP32.
    • Observación esperada: La terminal imprime «— ESP32 Field Monitor Initialization —«, seguido del SSID ESP32-FieldMonitor y la IP 192.168.4.1.
    • Condición de aprobación: El ESP32 no se bloquea ni entra en un bucle de reinicios.
  2. Verificar la transmisión SoftAP
    • Acción: Abre la configuración de Wi-Fi en un teléfono inteligente o computadora portátil.
    • Observación esperada: Una red llamada ESP32-FieldMonitor aparece en la lista de redes disponibles.
    • Condición de aprobación: Puedes conectarte a la red con éxito usando la contraseña admin1234.
  3. Verificar la carga del panel web
    • Acción: Abre un navegador web en el dispositivo conectado y navega a http://192.168.4.1.
    • Observación esperada: Se carga el «Field Monitor Dashboard» (Panel del Monitor de Campo), mostrando una tarjeta estilizada. El monitor serie registra «Dashboard accessed by a client.» (Panel accedido por un cliente).
    • Condición de aprobación: La interfaz de usuario se renderiza correctamente sin CSS roto.
  4. Verificar entrada física y salida LED
    • *Acción

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: ¿Qué microcontrolador se utiliza como Punto de Acceso por Software (SoftAP) en este proyecto?




Pregunta 2: ¿Cuál es el propósito principal del proyecto descrito?




Pregunta 3: ¿Qué tecnología de red utiliza el ESP32 para transmitir la red Wi-Fi?




Pregunta 4: ¿Qué tipo de dispositivo se utiliza típicamente para visualizar el panel de diagnóstico?




Pregunta 5: ¿Por qué es útil este sistema en campos agrícolas remotos o sótanos industriales?




Pregunta 6: ¿Qué ventaja de seguridad ofrece el registro de fallas de la máquina a través de este sistema?




Pregunta 7: ¿Qué significa que la Interfaz Hombre-Máquina (HMI) esté 'aislada' (air-gapped)?




Pregunta 8: ¿Cómo afecta el aislamiento de internet a la ciberseguridad del sistema?




Pregunta 9: ¿Con qué tipo de componentes industriales se interconecta el sistema para monitorear estados de forma segura?




Pregunta 10: ¿Cómo accede el personal al panel de diagnóstico desde su dispositivo móvil?




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:


Caso práctico: alarma ultrasónica con Arduino UNO

Caso práctico: alarma ultrasónica con Arduino UNO — hero

Objetivo y caso de uso

Qué construirás: Construirás un prototipo educativo de alarma de distancia de estacionamiento ultrasónica que mide la proximidad del vehículo y proporciona retroalimentación visual y acústica en tiempo real. El sistema utiliza lógica no bloqueante para calcular simultáneamente la distancia, actualizar una pantalla LCD y modular la frecuencia de los pitidos de un zumbador según la proximidad.

Por qué es importante / Casos de uso

  • Estacionamiento en garaje residencial: Ayuda a visualizar la distancia desde una pared para evitar daños en el parachoques (por ejemplo, alertando con precisión a los 30 cm).
  • Fundamentos de ADAS: Demuestra los principios fundamentales detrás de los sensores de estacionamiento automotrices comerciales.
  • Lógica no bloqueante: Reemplaza las rudimentarias llamadas delay() con máquinas de estado basadas en millis(), permitiendo lecturas concurrentes del sensor y actualizaciones de la pantalla a 10+ Hz sin bloqueos.

Resultado esperado

  • Una alarma de proximidad funcional con < 50ms de latencia entre la detección del objeto y la respuesta del zumbador.
  • Una interfaz LCD que muestra métricas de distancia en tiempo real (cm) y advertencias de estado dinámicas.
  • Un zumbador pasivo que escala su frecuencia de pulso dinámicamente, culminando en un tono continuo y sólido a < 10 cm.

Audiencia: Estudiantes de sistemas embebidos y aficionados a la electrónica automotriz; Nivel: Intermedio

Arquitectura/flujo: Sensor ultrasónico (Trigger/Echo) → Microcontrolador (Máquina de estados no bloqueante) → Pantalla LCD I2C & Zumbador pasivo PWM

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: 3 apartados, 3 tablas y 2 bloques de código detectados antes de publicar.
  • Código comprobado: 1 Arduino/arduino-cli compile, 1 Bash/copy-paste checks.
  • Catálogo soportado: el texto se contrastó contra los perfiles de dispositivo validables de Prometeo y 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, no un producto certificado. Antes de encender la configuración, verifica la disposición de pines de tu revisión exacta de la placa ULX3S, mantén las señales de E/S de la FPGA a 3.3 V, nunca conectes 5 V directamente a los pines de E/S, desconecta la alimentación antes de cambiar el cableado y usa fuentes externas adecuadas para cargas, motores o servos, compartiendo la tierra solo cuando el cableado lo requiera.

Diagrama de bloques conceptual

Vista de alto nivel: qué entra, qué procesa cada bloque y qué sale del sistema.

Arquitectura funcional

Botones ULX3S

Sincronizador/antirrebote

Selector de modo

Generador periodo 20 ms

Comparador ancho de pulso

Salida PWM 50 Hz

Servo SG90

Flujo conceptual de control: entrada de botones, selección de modo, temporización PWM y movimiento del servo.

Ruta de validación

Verilog fuente

Verilator lint/testbench

Yosys síntesis

nextpnr-ecp5

ecppack bitstream

ULX3S programada

La validación automática comprueba sintaxis, simulación/lint y compatibilidad con la toolchain ULX3S/ECP5.

Requisitos previos

Para completar con éxito este tutorial, debes tener:
* Una comprensión básica de la sintaxis de C++ (variables, declaraciones if/else, funciones).
* Familiaridad con la creación de prototipos en protoboard y conexiones de cables puente.
* Una computadora con Windows, macOS o Linux con una interfaz de línea de comandos.
* La arduino-cli (Interfaz de línea de comandos de Arduino) instalada y añadida al PATH de tu sistema.

Materiales

Necesitarás los componentes exactos enumerados a continuación para garantizar la compatibilidad con el código proporcionado y las instrucciones de cableado:
* Microcontrolador: 1x Arduino UNO R3 (ATmega328P).
* Sensor: 1x Sensor ultrasónico HC-SR04.
* Pantalla: 1x LCD 16×2 HD44780 (interfaz paralela estándar, sin mochila I2C).
* Audio: 1x Zumbador pasivo (requiere una señal de frecuencia para generar sonido).
* Componentes: 1x Potenciómetro de 10kΩ (para el ajuste de contraste del LCD), 1x Resistencia de 220Ω (para limitar la corriente de la retroiluminación del LCD).
* Prototipado: 1x Protoboard estándar y un juego de cables puente macho a macho.
* Alimentación/Datos: 1x Cable USB Tipo-A a Tipo-B.

Configuración/Conexión

El LCD HD44780 utiliza una interfaz paralela de 4 bits para ahorrar pines digitales en el Arduino. El HC-SR04 requiere un pin para activar el pulso ultrasónico y otro para leer el eco de retorno.

Distribución de energía

  1. Conecta el pin de 5V del Arduino al riel de alimentación positivo de la protoboard.
  2. Conecta el pin GND del Arduino al riel de tierra de la protoboard.

Conexiones del sensor ultrasónico HC-SR04

Pin HC-SR04 Pin Arduino UNO R3 Descripción
VCC 5V (Riel de alimentación) Fuente de alimentación de 5V
Trig Pin Digital 9 Emite el pulso de disparo de 10µs
Echo Pin Digital 10 Recibe el ancho de pulso de retorno
GND GND (Riel de tierra) Conexión a tierra

Conexiones del zumbador pasivo

Pin del Zumbador Pin Arduino UNO R3 Descripción
Positivo (+) Pin Digital 8 Salida de PWM/Frecuencia vía tone()
Negativo (-) GND (Riel de tierra) Conexión a tierra

Conexiones del LCD 16×2 HD44780

Nota: Las patas exteriores del potenciómetro de 10kΩ se conectan a 5V y GND. El cursor central se conecta al pin V0 del LCD para ajustar el contraste del texto.

Pin LCD Nombre Conexión Arduino / Componente Descripción
1 VSS GND (Riel de tierra) Tierra
2 VDD 5V (Riel de alimentación) Alimentación lógica de 5V
3 V0 Cursor central del potenciómetro Ajuste de contraste
4 RS Pin Digital 12 Selección de registro (Register Select)
5 RW GND (Riel de tierra) Lectura/Escritura (conectado a bajo para solo escritura)
6 E Pin Digital 11 Pin de habilitación (Enable)
11 D4 Pin Digital 5 Línea de datos 4
12 D5 Pin Digital 4 Línea de datos 5
13 D6 Pin Digital 3 Línea de datos 6
14 D7 Pin Digital 2 Línea de datos 7
15 A 5V a través de resistencia de 220Ω Ánodo de retroiluminación (+)
16 K GND (Riel de tierra) Cátodo de retroiluminación (-)

Código validado

Crea un nuevo directorio llamado ParkingAlarm y guarda el siguiente código como ParkingAlarm.ino. Las definiciones de pines y los umbrales se incluyen en la parte superior del archivo para facilitar el ajuste.

#include <LiquidCrystal.h>

// -----------------------------------------
// Pin Definitions
// -----------------------------------------
#define TRIG_PIN 9
#define ECHO_PIN 10
#define BUZZER_PIN 8

// HD44780 LCD (4-bit mode)
#define LCD_RS 12
#define LCD_EN 11
#define LCD_D4 5
#define LCD_D5 4
#define LCD_D6 3
#define LCD_D7 2

// -----------------------------------------
// System Thresholds (in centimeters)
// -----------------------------------------
#define DIST_MAX   200  // Maximum reliable distance to process
#define DIST_WARN  100  // Start warning (slow beep)
#define DIST_ALARM 50   // Start alarm (dynamic fast beep)
#define DIST_STOP  10   // Stop immediately (continuous tone)

// Initialize the LCD library with the interface pins
LiquidCrystal lcd(LCD_RS, LCD_EN, LCD_D4, LCD_D5, LCD_D6, LCD_D7);

// Variables for non-blocking buzzer timing
unsigned long previousBuzzerMillis = 0;
bool buzzerState = false;

void setup() {
    // Initialize Serial for debugging
    Serial.begin(115200);

    // Configure hardware pins
    pinMode(TRIG_PIN, OUTPUT);
    pinMode(ECHO_PIN, INPUT);
    pinMode(BUZZER_PIN, OUTPUT);

    // Initialize the LCD's columns and rows
    lcd.begin(16, 2);

    // Display a startup message
    lcd.setCursor(0, 0);
    lcd.print("Parking Assist");
    lcd.setCursor(0, 1);
    lcd.print("System Booting..");
    delay(2000); // 2-second boot delay is acceptable here
    lcd.clear();
}

// Function to trigger the HC-SR04 and calculate distance
long readDistance() {
    // Ensure the trigger pin is low before pulsing
    digitalWrite(TRIG_PIN, LOW);
    delayMicroseconds(2);

    // Send a 10 microsecond high pulse to trigger the sensor
    digitalWrite(TRIG_PIN, HIGH);
    delayMicroseconds(10);
    digitalWrite(TRIG_PIN, LOW);

    // Read the echo pin; timeout after 30,000 microseconds (approx 5 meters)
    long duration = pulseIn(ECHO_PIN, HIGH, 30000);

    // If timeout occurred, return -1 to indicate out of range
    if (duration == 0) {
        return -1;
    }

    // Calculate distance in centimeters
    // Speed of sound is 343 m/s or 0.0343 cm/microsecond
    // Divide by 2 to account for the round trip (ping and echo)
    return (duration * 0.0343) / 2;
}

void loop() {
    long distance = readDistance();
    unsigned long currentMillis = millis();

    // Debug output
    Serial.print("Distance: ");
    Serial.println(distance);

    // Top row: Display distance
    lcd.setCursor(0, 0);
    if (distance == -1 || distance > DIST_MAX) {
        lcd.print("Out of Range    "); // Pad with spaces to clear old chars
        noTone(BUZZER_PIN);

        lcd.setCursor(0, 1);
        lcd.print("Status: STANDBY ");
    } else {
        lcd.print("Dist: ");
        if (distance < 100) lcd.print(" "); // Alignment padding
        if (distance < 10)  lcd.print(" "); // Alignment padding
        lcd.print(distance);
        lcd.print(" cm      ");

        // Bottom row: Display status and handle buzzer logic
        lcd.setCursor(0, 1);

        if (distance > DIST_WARN) {
            // Safe zone
            lcd.print("Status: SAFE    ");
            noTone(BUZZER_PIN);

        } else if (distance > DIST_ALARM) {
            // Warning zone: Slow beep
            lcd.print("Status: SLOW    ");
            if (currentMillis - previousBuzzerMillis >= 500) { // 500ms interval
                previousBuzzerMillis = currentMillis;
                buzzerState = !buzzerState;
                if (buzzerState) {
                    tone(BUZZER_PIN, 1000); // 1000 Hz pitch
                } else {
                    noTone(BUZZER_PIN);
                }
            }

        } else if (distance > DIST_STOP) {
            // Alarm zone: Dynamic fast beep
            lcd.print("Status: ALARM   ");

            // Map the distance to a beep interval (closer = faster beep)
            // 50cm -> 400ms interval, 10cm -> 50ms interval
            int beepInterval = map(distance, DIST_STOP, DIST_ALARM, 50, 400);

            if (currentMillis - previousBuzzerMillis >= beepInterval) {
                previousBuzzerMillis = currentMillis;
                buzzerState = !buzzerState;
                if (buzzerState) {
                    tone(BUZZER_PIN, 1500); // 1500 Hz pitch
                } else {
                    noTone(BUZZER_PIN);
                }
            }

        } else {
            // Stop zone: Continuous tone
            lcd.print("Status: STOP!   ");
            tone(BUZZER_PIN, 2000); // 2000 Hz high pitch alert
        }
    }

    // Small delay to stabilize the loop and prevent LCD flickering
    delay(50);
}

Comandos de compilación/flasheo/ejecución

Usaremos la CLI de Arduino para compilar y cargar el código. Abre tu terminal, navega hasta el directorio que contiene la carpeta ParkingAlarm y ejecuta los siguientes comandos.

Nota: Reemplaza /dev/ttyACM0 con tu puerto serie real (por ejemplo, COM3 en Windows o /dev/cu.usbmodem14101 en macOS).

# Update the core index
arduino-cli core update-index

# Install the AVR core
arduino-cli core install arduino:avr

# Compile the sketch
arduino-cli compile --fqbn arduino:avr:uno ParkingAlarm

# Upload to the board
arduino-cli upload --fqbn arduino:avr:uno --port /dev/ttyACM0 ParkingAlarm

# Monitor serial output
arduino-cli monitor --port /dev/ttyACM0 --config baudrate=115200

Validación paso a paso

Sigue estos puntos de control para asegurarte de que tu prototipo funciona correctamente y cumple con las afirmaciones de rendimiento.

  1. Comprobación de inicialización del LCD
    • Acción: Suministra energía al Arduino a través de USB.
    • Evidencia esperada: La retroiluminación del LCD se enciende y el texto «Parking Assist» seguido de «System Booting..» aparece durante 2 segundos antes de borrarse. Ajusta el potenciómetro si el texto no es visible.
  2. Validación de precisión de distancia
    • Acción: Coloca un objeto plano y rígido (como un trozo de cartón o un libro) exactamente a 50 cm de distancia del sensor, medido con una cinta métrica física.
    • Evidencia esperada: El LCD debería mostrar Dist: 50 cm (una tolerancia de ± 1-2 cm es aceptable debido a que la velocidad del sonido varía ligeramente con la temperatura ambiente).
  3. Validación de tasa de actualización a 10 Hz
    • Acción: Observa las marcas de tiempo de salida del Monitor Serie.
    • Evidencia esperada: El delay(50) más el tiempo de espera del sensor limitan el bucle a aproximadamente 100 ms por ciclo. Deberías ver aproximadamente 10 lecturas de distancia impresas en la consola serie cada segundo, lo que confirma la tasa objetivo de 10 Hz.
  4. Validación de zona acústica
    • Acción: Mueve el objeto rígido progresivamente más cerca del sensor, comenzando desde 150 cm hasta 5 cm.
    • Evidencia esperada:
      • > 100 cm: El LCD muestra «Status: SAFE», el zumbador está completamente en silencio.
      • 99 cm a 51 cm: El LCD muestra «Status: SLOW», el zumbador emite 1 pitido constante por segundo.
      • 50 cm a 11 cm: El LCD muestra «Status: ALARM», la frecuencia de los pitidos del zumbador aumenta dinámicamente a medida que el objeto se acerca.
      • <= 10 cm: El LCD muestra «Status: STOP!», el zumbador emite un tono agudo, continuo e ininterrumpido.

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: ¿Qué tipo de prototipo educativo se construirá según el texto?




Pregunta 2: ¿Qué tipo de retroalimentación proporciona el sistema en tiempo real?




Pregunta 3: ¿Qué componente se utiliza para emitir los pitidos según la proximidad?




Pregunta 4: ¿Qué tipo de lógica utiliza el sistema para realizar múltiples tareas simultáneamente?




Pregunta 5: ¿Cuál es un caso de uso mencionado para este prototipo en un entorno residencial?




Pregunta 6: ¿A qué distancia de ejemplo menciona el texto que el sistema puede alertar con precisión en un garaje?




Pregunta 7: ¿Qué principios fundamentales demuestra este prototipo?




Pregunta 8: ¿Qué función de programación rudimentaria se reemplaza para evitar bloqueos en el sistema?




Pregunta 9: ¿Qué función se utiliza en las máquinas de estado para permitir lecturas concurrentes sin bloqueos?




Pregunta 10: ¿Cuál es la latencia esperada entre la detección del objeto y la respuesta del zumbador?




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:


Caso práctico: registrador de eventos en SD con Arduino UNO

Caso práctico: registrador de eventos en SD con Arduino UNO — hero

Objetivo y caso de uso

Qué construirás: Construirás un registrador de datos de eventos independiente y basado en hardware que registra activadores discretos manuales directamente en una tarjeta microSD local. Cada evento registrado se guarda de manera confiable con un ID secuencial y una marca de tiempo precisa con resolución de milisegundos.

Por qué es importante / Casos de uso

  • Conteo de inventario y almacén: Registra los conteos de artículos físicos en ubicaciones aisladas donde la infraestructura Wi-Fi o RF no está disponible.
  • Seguimiento de ciclos de máquinas: Conecta un interruptor de límite mecánico para monitorear y registrar el número exacto de veces que una máquina industrial completa un ciclo físico.
  • Registro de acceso: Interfaz con un interruptor de láminas magnético (reed switch) para registrar el momento exacto y la frecuencia de apertura de puertas, independientemente de la seguridad en red.
  • Recopilación de datos de campo sin conexión: Permite a los investigadores ambientales registrar manualmente eventos de observación durante recorridos de campo remotos.

Resultado esperado

  • Una entrada mecánica totalmente antirrebote (ej. 50ms de antirrebote por software) que evita que las pulsaciones únicas se registren como múltiples activadores falsos.
  • Persistencia de datos confiable y sin conexión en una tarjeta microSD a través de SPI con ciclos de escritura de baja latencia (<10ms).
  • Registros de eventos secuenciales y precisos formateados como CSV para un fácil procesamiento posterior.

Audiencia: Desarrolladores de sistemas embebidos e ingenieros de hardware; Nivel: Intermedio

Arquitectura/flujo: Interruptor mecánico (Entrada) → Microcontrolador (Interrupción de hardware + Lógica antirrebote) → Temporizador de milisegundos → Interfaz SPI → Tarjeta microSD (Registro CSV)

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: 3 apartados, 3 tablas y 2 bloques de código detectados antes de publicar.
  • Código comprobado: 1 Python/py_compile, 1 Arduino/arduino-cli compile.
  • Catálogo soportado: el texto se contrastó contra los perfiles de dispositivo validables de Prometeo y 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, no un producto certificado. Antes de encender la configuración, verifique la distribución de pines de la revisión exacta de su placa ULX3S, mantenga las señales de E/S de la FPGA a 3.3 V, nunca conecte 5 V directamente a los pines de E/S, desconecte la alimentación antes de cambiar el cableado y use fuentes externas adecuadas para cargas, motores o servos mientras comparte la tierra solo cuando el cableado lo requiera.

Diagrama de bloques conceptual

Vista de alto nivel: qué entra, qué procesa cada bloque y qué sale del sistema.

Arquitectura funcional

Interruptor mecánico (Entrada)

Microcontrolador

Temporizador de milisegundos

Interfaz SPI

Tarjeta microSD (Registro CSV)

Flujo conceptual de señales y responsabilidades entre bloques del dispositivo.

Ruta de validación

Sketch

arduino-cli compile

Upload

Prueba funcional

Resumen conceptual de las herramientas usadas para comprobar el material publicado.

Requisitos previos

Antes de comenzar este proyecto, asegúrese de tener listo lo siguiente:
* Una comprensión básica de las operaciones de Entrada/Salida de Propósito General (GPIO) de Arduino, específicamente lecturas y escrituras digitales.
* Familiaridad con el concepto de rebote de interruptores y por qué los contactos mecánicos requieren un mecanismo antirrebote por software o hardware.
* Un ordenador con Windows, macOS o Linux con una terminal de interfaz de línea de comandos.
* La arduino-cli (Interfaz de línea de comandos de Arduino) instalada y agregada al PATH de su sistema.
* Una tarjeta microSD (32GB o menor) recién formateada en FAT16 o FAT32. La biblioteca SD estándar de Arduino no admite formatos exFAT que normalmente se encuentran en tarjetas de 64GB+.

Materiales

  • Microcontrolador: Arduino UNO R3 (ATmega328P).
  • Almacenamiento: Módulo SPI para microSD (Asegúrese de que sea un módulo con un regulador de voltaje de 3.3V incorporado y un cambiador de nivel lógico, ya que el Arduino UNO R3 opera con lógica de 5V).
  • Entrada: Pulsador táctil momentáneo estándar de 4 o 2 pines.
  • Retroalimentación: LED de estado de 5mm o 3mm (cualquier color).
  • Componentes pasivos: 1x resistencia de 220 ohmios (para el LED). Utilizaremos la resistencia pull-up interna del Arduino para el pulsador.
  • Prototipado: Placa de pruebas (breadboard) y cables puente macho a macho surtidos.
  • Datos/Energía: Cable USB Tipo A a Tipo B.

Configuración/Conexión

El Arduino UNO R3 se comunica con el módulo microSD utilizando el protocolo de Interfaz Periférica Serial (SPI). El ATmega328P tiene pines SPI de hardware dedicados que deben usarse para un rendimiento óptimo.

Cableado del módulo SPI para microSD:

Pin del módulo microSD Pin del Arduino UNO R3 Descripción de la función
VCC 5V Fuente de alimentación (el módulo lo reduce a 3.3V)
GND GND Tierra común
MISO Pin 12 Master In Slave Out (Datos de la SD al Arduino)
MOSI Pin 11 Master Out Slave In (Datos del Arduino a la SD)
SCK Pin 13 Serial Clock (Señal de reloj generada por el Arduino)
CS Pin 4 Chip Select (Indica a la tarjeta SD que escuche)

Cableado del pulsador:
* Conecte un terminal del pulsador al Pin 2 del Arduino.
* Conecte el terminal opuesto del pulsador a GND.
* Nota: No se requiere resistencia externa. Configuraremos el Pin 2 usando INPUT_PULLUP en el código, lo que conecta una resistencia interna de 20k ohmios a 5V dentro del ATmega328P. Cuando se presiona el botón, el pin lee LOW.

Cableado del LED de estado:
* Conecte la pata más larga (Ánodo) del LED de estado al Pin 8 del Arduino.
* Conecte la pata más corta (Cátodo) a un extremo de la resistencia de 220 ohmios.
* Conecte el otro extremo de la resistencia de 220 ohmios a GND.

Código validado

La siguiente sección contiene el código fuente completo requerido para el registrador de eventos, así como un script de Python complementario para analizar los datos generados.

Sketch de Arduino: SD_Event_Logger.ino

Cree un nuevo directorio llamado SD_Event_Logger y guarde el siguiente código dentro de él como SD_Event_Logger.ino.

Este código maneja el mecanismo antirrebote del botón mecánico, inicializa la comunicación SPI y agrega los datos a la tarjeta SD. Utiliza las bibliotecas estándar SD.h y SPI.h incluidas con el núcleo de Arduino.

Vista pública parcial del archivo validado. El código completo se muestra a miembros y en PDF/Print.

/*
 * SD Event Data Logger
 * Target: Arduino UNO R3 (ATmega328P)
 * Objective: Log debounced button presses to a microSD card over SPI.
 */

#include <SPI.h>
#include <SD.h>

// Pin Definitions
const int chipSelect = 4;
const int buttonPin = 2;
const int ledPin = 8;

// State Variables
unsigned long eventCount = 0;
int buttonState;
int lastButtonState = HIGH; // HIGH because we use INPUT_PULLUP

// Timing Variables for Debounce
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 50; // 50 milliseconds

void setup() {
  // Initialize Serial for debugging
  Serial.begin(9600);
  while (!Serial) {
    ; // Wait for serial port to connect
  }

  // Configure Pins
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);

  Serial.println("Initializing SD card...");

  // Initialize SD Card
  if (!SD.begin(chipSelect)) {
    Serial.println("Critical Error: SD card initialization failed!");
    Serial.println("Check wiring, formatting (FAT16/32), and card insertion.");
    // Trap execution in an infinite loop and blink LED rapidly
    while (true) {
      digitalWrite(ledPin, HIGH);
      delay(100);
      digitalWrite(ledPin, LOW);
      delay(100);
    }
  }

  Serial.println("SD card initialized successfully.");

  // Optional: Write a CSV header if the file does not exist
  if (!SD.exists("events.csv")) {
    File dataFile = SD.open("events.csv", FILE_WRITE);
    if (dataFile) {
      dataFile.println("Event_ID,Uptime_ms");
      dataFile.close();
      Serial.println("Created new events.csv with headers.");
    } else {
      Serial.println("Error: Could not create events.csv");
    }
  }
}
// ... continúa para miembros en el código completo validado ...

🔒 Parte del código validado es premium. Con el pase de 7 días o la suscripción mensual podrás consultar el archivo completo validado.

/*
 * SD Event Data Logger
 * Target: Arduino UNO R3 (ATmega328P)
 * Objective: Log debounced button presses to a microSD card over SPI.
 */

#include <SPI.h>
#include <SD.h>

// Pin Definitions
const int chipSelect = 4;
const int buttonPin = 2;
const int ledPin = 8;

// State Variables
unsigned long eventCount = 0;
int buttonState;
int lastButtonState = HIGH; // HIGH because we use INPUT_PULLUP

// Timing Variables for Debounce
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 50; // 50 milliseconds

void setup() {
  // Initialize Serial for debugging
  Serial.begin(9600);
  while (!Serial) {
    ; // Wait for serial port to connect
  }

  // Configure Pins
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);

  Serial.println("Initializing SD card...");

  // Initialize SD Card
  if (!SD.begin(chipSelect)) {
    Serial.println("Critical Error: SD card initialization failed!");
    Serial.println("Check wiring, formatting (FAT16/32), and card insertion.");
    // Trap execution in an infinite loop and blink LED rapidly
    while (true) {
      digitalWrite(ledPin, HIGH);
      delay(100);
      digitalWrite(ledPin, LOW);
      delay(100);
    }
  }

  Serial.println("SD card initialized successfully.");

  // Optional: Write a CSV header if the file does not exist
  if (!SD.exists("events.csv")) {
    File dataFile = SD.open("events.csv", FILE_WRITE);
    if (dataFile) {
      dataFile.println("Event_ID,Uptime_ms");
      dataFile.close();
      Serial.println("Created new events.csv with headers.");
    } else {
      Serial.println("Error: Could not create events.csv");
    }
  }
}

void loop() {
  // Read the state of the switch into a local variable:
  int reading = digitalRead(buttonPin);

  // Check to see if you just pressed the button
  // (i.e. the input went from HIGH to LOW), and you've waited long enough
  // since the last press to ignore any noise:

  // If the switch changed, due to noise or pressing:
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    // whatever the reading is at, it's been there for longer than the debounce
    // delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading != buttonState) {
      buttonState = reading;

      // only trigger an event if the new button state is LOW (pressed)
      if (buttonState == LOW) {
        logEventToSD();
      }
    }
  }

  // save the reading. Next time through the loop, it'll be the lastButtonState:
  lastButtonState = reading;
}

void logEventToSD() {
  eventCount++;
  unsigned long currentTimestamp = millis();

  // Open the file. Note that only one file can be open at a time.
  // The filename must follow the 8.3 format (max 8 chars name, 3 chars extension)
  File dataFile = SD.open("events.csv", FILE_WRITE);

  // If the file is available, write to it:
  if (dataFile) {
    dataFile.print(eventCount);
    dataFile.print(",");
    dataFile.println(currentTimestamp);
    dataFile.close();

    // Print to the serial port too:
    Serial.print("Logged -> Event: ");
    Serial.print(eventCount);
    Serial.print(" | Time: ");
    Serial.println(currentTimestamp);

    // Visual feedback: Quick pulse on the status LED
    digitalWrite(ledPin, HIGH);
    delay(150); // Short blocking delay is acceptable here to stretch the visual flash
    digitalWrite(ledPin, LOW);
  } 
  else {
    // If the file isn't open, pop up an error:
    Serial.println("Error: Failed to open events.csv for writing.");

    // Visual feedback: Three slow flashes indicating a write error
    for (int i = 0; i < 3; i++) {
      digitalWrite(ledPin, HIGH);
      delay(300);
      digitalWrite(ledPin, LOW);
      delay(300);
    }
  }
}

Script de análisis en Python: analyze_events.py

Guarde este archivo en su ordenador como analyze_events.py. Una vez que haya recopilado datos en su tarjeta SD, inserte la tarjeta SD en su ordenador y ejecute este script contra el archivo events.csv para analizar las métricas básicas.

#!/usr/bin/env python3
"""
SD Event Logger Analysis Tool
Objective: Parse the events.csv file generated by the Arduino UNO R3 prototype
and calculate basic duration metrics.
"""

import csv
import sys

def analyze_log(filename):
    try:
        with open(filename, 'r') as file:
            reader = csv.reader(file)
            header = next(reader) # Skip the header row

            events = list(reader)

        total_events = len(events)
        print(f"--- Log Analysis for {filename} ---")
        print(f"Total discrete events logged: {total_events}")

        if total_events >= 2:
            first_time_ms = int(events[0][1])
            last_time_ms = int(events[-1][1])

            duration_ms = last_time_ms - first_time_ms
            duration_sec = duration_ms / 1000.0

            print(f"First event registered at: {first_time_ms} ms")
            print(f"Last event registered at:  {last_time_ms} ms")
            print(f"Total duration between first and last event: {duration_sec:.2f} seconds")

            if duration_sec > 0:
                frequency = total_events / duration_sec
                print(f"Average event frequency: {frequency:.2f} events/second")

    except FileNotFoundError:
        print(f"Error: Could not find '{filename}'. Ensure the path is correct.")
    except ValueError as ve:
        print(f"Error parsing data (likely malformed CSV row): {ve}")
    except Exception as e:
        print(f"An unexpected error occurred: {e}")

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python analyze_events.py <path_to_events.csv>")
    else:
        analyze_log(sys.argv[1])

Comandos de compilación/flasheo/ejecución

Utilice la CLI de Arduino para compilar y cargar el código. Conecte su Arduino UNO R3 a su ordenador a través de USB. Identifique el puerto (ej., COM3 en Windows o /dev/ttyACM0 en Linux/macOS).

Propósito del comando Comando CLI
Actualizar índice del núcleo arduino-cli core update-index
Instalar núcleo AVR arduino-cli core install arduino:avr
Compilar sketch arduino-cli compile --fqbn arduino:avr:uno SD_Event_Logger
Subir a la placa arduino-cli upload --fqbn arduino:avr:uno --port <PORT> SD_Event_Logger
Monitor serial arduino-cli monitor --port <PORT> --config baudrate=9600

Flujo de trabajo:
1. Abra su terminal y navegue hasta el directorio padre que contiene la carpeta SD_Event_Logger.
2. Ejecute los comandos de actualización del índice y de instalación del núcleo para asegurarse de que su entorno esté listo.
3. Compile el sketch utilizando la bandera --fqbn para el UNO.
4. Reemplace <PORT> con su puerto serial real y ejecute el comando de subida.
5. Inicie inmediatamente el monitor serial para observar el proceso de inicialización.

Validación paso a paso

Siga estos puntos de control para asegurarse de que su prototipo esté funcionando correctamente.

  1. Comprobación de inicialización de la SD
    • Observación: Abra el monitor serial inmediatamente después de encender la placa.
    • Condición de aprobación: El monitor serial muestra «Initializing SD card…» seguido de «SD card initialized successfully.» El LED de estado permanece apagado.
  2. Comprobación de manejo de errores por falta de tarjeta
    • Observación: Desconecte la alimentación, expulse la tarjeta microSD, restaure la alimentación y observe el LED de estado.
    • Condición de aprobación: El monitor serial muestra «Critical Error: SD card initialization failed!» y el LED de estado parpadea rápida y continuamente.
  3. Comprobación del activador de eventos
    • Observación: Vuelva a insertar la tarjeta SD, encienda la placa y presione el pulsador una vez.
    • Condición de aprobación: El LED de estado parpadea brevemente (150ms). El monitor serial imprime Logged -> Event: 1 | Time: [timestamp].
  4. Comprobación de la lógica antirrebote
    • Observación: Mantenga presionado el pulsador, muévalo ligeramente sin soltarlo por completo, luego suéltelo.
    • Condición de aprobación: Solo se registra un evento por cada ciclo distinto de presionar y soltar. El recuento de eventos se incrementa suavemente sin saltar números (ej., saltando del 1 al 4).
  5. Comprobación de integridad de datos
    • Observación: Apague el Arduino. Retire la tarjeta SD, insértela en su ordenador y ejecute el script de análisis en Python: python analyze_events.py /ruta/a/la/SD/events.csv.
    • Condición de aprobación: El script analiza con éxito el archivo, informando el número total correcto de eventos y calculando la duración de tiempo entre la primera y la última pulsación.

Solución de problemas

Síntoma Causa probable Solución
La inicialización de la SD falla (Rápido

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: ¿Qué tipo de dispositivo se construirá según el artículo?




Pregunta 2: ¿Dónde se guardan los registros de los eventos en este dispositivo?




Pregunta 3: ¿Qué información específica se guarda con cada evento registrado?




Pregunta 4: ¿Por qué es útil este dispositivo para el conteo de inventario en almacenes aislados?




Pregunta 5: ¿Qué componente se sugiere conectar para el seguimiento de ciclos de máquinas industriales?




Pregunta 6: En el caso de registro de acceso, ¿qué tipo de sensor se menciona para registrar la apertura de puertas?




Pregunta 7: ¿Cuál es una ventaja del registro de acceso propuesto en el texto?




Pregunta 8: ¿Para qué pueden usar este dispositivo los investigadores ambientales?




Pregunta 9: ¿Qué tipo de activadores registra principalmente el dispositivo?




Pregunta 10: ¿Cuál es la resolución de la marca de tiempo que se guarda con cada evento?




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:


Caso práctico: probador de servos con Arduino UNO

Caso práctico: probador de servos con Arduino UNO — hero

Objetivo y caso de uso

Qué construirás: Un probador de calibración de servos independiente y multimodo que permite a los usuarios hacer un barrido manual, centrar automáticamente (1500µs) y probar los límites físicos de los servos estándar de hobby.

Por qué es importante / Casos de uso

  • Configuración de vehículos RC: Centrar perfectamente los servos de dirección y aceleración antes de colocar los brazos mecánicos (horns) del servo para garantizar una geometría de dirección simétrica.
  • Ensamblaje de brazos robóticos: Identificar los valores exactos en microsegundos de PWM (por ejemplo, de 500µs a 2500µs) para los límites físicos de las articulaciones y evitar así atascos mecánicos bruscos y que el motor se queme en tu código.
  • Diagnóstico de hardware: Probar rápidamente servos recuperados o sospechosos en busca de puntos muertos, engranajes desgastados o vibración excesiva (jitter) sin necesidad de escribir o flashear un script de prueba personalizado.
  • Prototipado rápido: Establecer una herramienta de banco permanente para accionar manualmente los enlaces y probar mecanismos durante la fase de diseño mecánico.

Resultado esperado

  • Una interfaz de control de hardware confiable que utiliza un potenciómetro leído por ADC para realizar ajustes de ángulo manuales suaves y de baja latencia.
  • Una entrada de botón con eliminación de rebotes (debounce) por software que alterna de manera fluida la máquina de estados del microcontrolador entre tres modos operativos: Control manual, Centro (90°) y Barrido automático (Auto Sweep).

Audiencia: Aficionados al hardware, desarrolladores de robótica y constructores de RC; Nivel: Intermedio

Arquitectura/flujo: Entradas de usuario (Potenciómetro y botón) → Microcontrolador (Lectura ADC y lógica de debounce) → Salida de señal PWM de 50Hz → Accionamiento del servomotor

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: 3 apartados, 3 tablas y 2 bloques de código detectados antes de publicar.
  • Código comprobado: 1 Arduino/arduino-cli compile, 1 Bash/copy-paste checks.
  • Catálogo soportado: el texto se contrastó contra los perfiles de dispositivo validables de Prometeo y 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 prototipo está diseñado estrictamente para uso de laboratorio educativo y calibración de banco de pequeños servos de hobby sin carga.
* Límites de energía: No intente alimentar servos de alto torque (por ejemplo, MG995, MG996R) o brazos robóticos multiservo directamente desde el pin de 5V del Arduino UNO. Hacerlo excederá los límites térmicos y de corriente del regulador de voltaje integrado, lo que podría destruir la placa Arduino o el puerto USB de su computadora.
* Peligros mecánicos: Incluso los servos pequeños pueden presentar un peligro de pellizco si están unidos a enlaces mecánicos rígidos. Realice siempre las pruebas de calibración iniciales con el brazo del servo (horn) desmontado o completamente libre de obstrucciones mecánicas.
* Idoneidad: Este dispositivo no es adecuado, ni está destinado a ser utilizado en sistemas de control críticos, aplicaciones RC aeroespaciales o cualquier entorno donde una falla pueda resultar en daños a la propiedad o lesiones.

Diagrama de bloques conceptual

Vista de alto nivel: qué entra, qué procesa cada bloque y qué sale del sistema.

Arquitectura funcional

Entradas de usuario (Potenciómetro y botón)

Microcontrolador (Lectura ADC y lógica de…

Salida de señal PWM de 50Hz

Accionamiento del servomotor

Flujo conceptual de señales y responsabilidades entre bloques del dispositivo.

Ruta de validación

Sketch

arduino-cli compile

Upload

Prueba funcional

Resumen conceptual de las herramientas usadas para comprobar el material publicado.

Requisitos previos

Para completar con éxito este tutorial, debe tener:
* Una comprensión básica de cómo utilizar una terminal o símbolo del sistema.
* Arduino CLI instalado en su estación de trabajo.
* Familiaridad con el prototipado básico en protoboard (comprender la continuidad a lo largo de los rieles de alimentación y las tiras de terminales).
* Comprensión de los conceptos básicos de la modulación por ancho de pulsos (PWM), específicamente el período estándar de 20 milisegundos y el ciclo de trabajo de 1–2 milisegundos utilizado por los servos estándar de hobby.

Materiales

Para este prototipo, necesitará exactamente los siguientes componentes:
* Arduino UNO R3 (ATmega328P): La placa del microcontrolador principal.
* Micro Servo SG90: Un servomotor estándar de hobby de 9 gramos.
* Potenciómetro de 10 kΩ: Un potenciómetro rotativo estándar (preferiblemente de variación lineal).
* Botón pulsador: Un interruptor táctil momentáneo estándar.
* Protoboard y cables puente (jumper wires): Para realizar conexiones sin soldadura.
* Cable USB Tipo A a Tipo B: Para conectar el Arduino UNO R3 a su computadora.

Configuración/Conexión

La configuración del hardware está diseñada para ser mínima, utilizando la resistencia pull-up interna del Arduino para el botón pulsador para reducir la cantidad de componentes externos.

Guía de cableado de componentes:

Componente Pin / Terminal Conexión Arduino UNO R3 Notas
Servo SG90 Cable Marrón / Negro GND Conexión a tierra.
Servo SG90 Cable Rojo 5V Conexión de alimentación (Solo para pruebas sin carga).
Servo SG90 Cable Naranja / Amarillo Pin Digital 9 Señal de control PWM.
Potenciómetro Terminal Izquierdo (Pin 1) 5V Voltaje de referencia para la lectura analógica.
Potenciómetro Terminal Central (Wiper) Pin Analógico A0 Salida de voltaje variable (0-5V).
Potenciómetro Terminal Derecho (Pin 3) GND Referencia a tierra.
Botón pulsador Terminal 1 Pin Digital 2 Señal de entrada.
Botón pulsador Terminal 2 GND Cierra el circuito a tierra cuando se presiona.

Nota sobre el botón pulsador: Conectamos el botón directamente entre el Pin Digital 2 y Tierra. En el software, configuraremos el Pin 2 con INPUT_PULLUP. Esto mantiene el pin en un nivel lógico ALTO (5V) internamente. Cuando se presiona el botón, conecta el pin a tierra, llevando el nivel lógico a BAJO.

Nota sobre la alimentación del servo: El SG90 es un servo pequeño. Cuando está completamente sin carga (sin enlaces mecánicos conectados), puede obtener energía de forma segura del pin de 5V del Arduino UNO, que es suministrada por la conexión USB. Si conecta una carga mecánica al servo, consumirá más corriente y puede provocar que el Arduino se reinicie.

Código validado

El proyecto requiere dos archivos. El primero es el sketch principal de Arduino que contiene la lógica. El segundo es un script de shell simple para automatizar el proceso de compilación y carga utilizando Arduino CLI.

Sketch principal: servo_tester.ino

Cree un directorio llamado servo_tester y guarde el siguiente código dentro de él como servo_tester.ino.

Vista pública parcial del archivo validado. El código completo se muestra a miembros y en PDF/Print.

/*
 * Project: Servo Calibration Tester
 * Target: Arduino UNO R3 (ATmega328P)
 * Description: A multi-mode servo testing tool. Uses a potentiometer for manual
 * control and a pushbutton to cycle between Manual, Center, and Sweep modes.
 */

#include <Servo.h>

// Pin Definitions
const int SERVO_PIN = 9;
const int POT_PIN = A0;
const int BUTTON_PIN = 2;

// Servo Object
Servo testServo;

// State Machine Variables
enum TesterMode {
  MODE_MANUAL = 0,
  MODE_CENTER = 1,
  MODE_SWEEP  = 2
};

TesterMode currentMode = MODE_MANUAL;

// Button Debouncing Variables
int buttonState = HIGH;             // Current reading from the input pin
int lastButtonState = HIGH;         // Previous reading from the input pin
unsigned long lastDebounceTime = 0; // The last time the output pin was toggled
const unsigned long debounceDelay = 50;   // Debounce time in milliseconds

// Servo Sweep Variables
int sweepAngle = 0;
int sweepDirection = 1;
unsigned long lastSweepUpdate = 0;
const unsigned long sweepInterval = 15; // Milliseconds between sweep steps

// Telemetry Variables
unsigned long lastTelemetryTime = 0;
const unsigned long telemetryInterval = 500; // Update Serial every 500ms

void setup() {
  // Initialize Serial Monitor
  Serial.begin(115200);
  while (!Serial) {
    ; // Wait for serial port to connect
  }

  Serial.println("Initializing Servo Calibration Tester...");

  // Configure Pins
  pinMode(POT_PIN, INPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP); // Use internal pull-up resistor

  // Attach Servo
  // Using standard min/max pulse widths (1000us to 2000us)
  // Some SG90 servos respond better to 500us to 2400us, adjust if necessary.
  testServo.attach(SERVO_PIN, 1000, 2000);

  // Set initial position
  testServo.write(90);
  Serial.println("System Ready. Mode: MANUAL");
}

void loop() {
  handleButton();
  updateServo();
  sendTelemetry();
}

void handleButton() {
  int reading = digitalRead(BUTTON_PIN);

  // Check if the button state has changed
  if (reading != lastButtonState) {
    lastDebounceTime = millis();
  }
// ... continúa para miembros en el código completo validado ...

🔒 Parte del código validado es premium. Con el pase de 7 días o la suscripción mensual podrás consultar el archivo completo validado.

/*
 * Project: Servo Calibration Tester
 * Target: Arduino UNO R3 (ATmega328P)
 * Description: A multi-mode servo testing tool. Uses a potentiometer for manual
 * control and a pushbutton to cycle between Manual, Center, and Sweep modes.
 */

#include <Servo.h>

// Pin Definitions
const int SERVO_PIN = 9;
const int POT_PIN = A0;
const int BUTTON_PIN = 2;

// Servo Object
Servo testServo;

// State Machine Variables
enum TesterMode {
  MODE_MANUAL = 0,
  MODE_CENTER = 1,
  MODE_SWEEP  = 2
};

TesterMode currentMode = MODE_MANUAL;

// Button Debouncing Variables
int buttonState = HIGH;             // Current reading from the input pin
int lastButtonState = HIGH;         // Previous reading from the input pin
unsigned long lastDebounceTime = 0; // The last time the output pin was toggled
const unsigned long debounceDelay = 50;   // Debounce time in milliseconds

// Servo Sweep Variables
int sweepAngle = 0;
int sweepDirection = 1;
unsigned long lastSweepUpdate = 0;
const unsigned long sweepInterval = 15; // Milliseconds between sweep steps

// Telemetry Variables
unsigned long lastTelemetryTime = 0;
const unsigned long telemetryInterval = 500; // Update Serial every 500ms

void setup() {
  // Initialize Serial Monitor
  Serial.begin(115200);
  while (!Serial) {
    ; // Wait for serial port to connect
  }

  Serial.println("Initializing Servo Calibration Tester...");

  // Configure Pins
  pinMode(POT_PIN, INPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP); // Use internal pull-up resistor

  // Attach Servo
  // Using standard min/max pulse widths (1000us to 2000us)
  // Some SG90 servos respond better to 500us to 2400us, adjust if necessary.
  testServo.attach(SERVO_PIN, 1000, 2000);

  // Set initial position
  testServo.write(90);
  Serial.println("System Ready. Mode: MANUAL");
}

void loop() {
  handleButton();
  updateServo();
  sendTelemetry();
}

void handleButton() {
  int reading = digitalRead(BUTTON_PIN);

  // Check if the button state has changed
  if (reading != lastButtonState) {
    lastDebounceTime = millis();
  }

  // If the state has been stable longer than the debounce delay
  if ((millis() - lastDebounceTime) > debounceDelay) {
    // If the button state has physically changed
    if (reading != buttonState) {
      buttonState = reading;

      // Only toggle mode if the new button state is LOW (pressed)
      if (buttonState == LOW) {
        cycleMode();
      }
    }
  }
  lastButtonState = reading;
}

void cycleMode() {
  if (currentMode == MODE_MANUAL) {
    currentMode = MODE_CENTER;
    Serial.println(">>> Mode Switched: CENTER (90 deg) <<<");
  } else if (currentMode == MODE_CENTER) {
    currentMode = MODE_SWEEP;
    Serial.println(">>> Mode Switched: AUTO-SWEEP <<<");
  } else {
    currentMode = MODE_MANUAL;
    Serial.println(">>> Mode Switched: MANUAL <<<");
  }
}

void updateServo() {
  switch (currentMode) {
    case MODE_MANUAL:
      {
        int potValue = analogRead(POT_PIN);
        // Map 10-bit ADC (0-1023) to Servo Angle (0-180)
        int targetAngle = map(potValue, 0, 1023, 0, 180);
        testServo.write(targetAngle);
      }
      break;

    case MODE_CENTER:
      {
        testServo.write(90);
      }
      break;

    case MODE_SWEEP:
      {
        if (millis() - lastSweepUpdate > sweepInterval) {
          lastSweepUpdate = millis();
          sweepAngle += sweepDirection;

          if (sweepAngle >= 180) {
            sweepAngle = 180;
            sweepDirection = -1;
          } else if (sweepAngle <= 0) {
            sweepAngle = 0;
            sweepDirection = 1;
          }
          testServo.write(sweepAngle);
        }
      }
      break;
  }
}

void sendTelemetry() {
  if (millis() - lastTelemetryTime > telemetryInterval) {
    lastTelemetryTime = millis();

    int currentAngle = testServo.read();
    int currentMicroseconds = testServo.readMicroseconds();

    Serial.print("Mode: ");
    switch (currentMode) {
      case MODE_MANUAL: Serial.print("MANUAL\t"); break;
      case MODE_CENTER: Serial.print("CENTER\t"); break;
      case MODE_SWEEP:  Serial.print("SWEEP \t"); break;
    }

    Serial.print(" | Angle: ");
    Serial.print(currentAngle);
    Serial.print(" deg | Pulse: ");
    Serial.print(currentMicroseconds);
    Serial.println(" us");
  }
}

Script de compilación: build.sh

Guarde este archivo en el directorio principal de servo_tester (o ajuste la ruta según corresponda). Hágalo ejecutable con chmod +x build.sh. Reemplace <PORT> con su puerto serie real (por ejemplo, /dev/ttyACM0 en Linux, COM3 en Windows).

#!/bin/bash

PORT="/dev/ttyACM0" # CHANGE THIS TO YOUR ACTUAL PORT
FQBN="arduino:avr:uno"
SKETCH_DIR="servo_tester"

echo "Updating Arduino core index..."
arduino-cli core update-index

echo "Installing AVR core if not present..."
arduino-cli core install arduino:avr

echo "Compiling sketch..."
arduino-cli compile --fqbn $FQBN $SKETCH_DIR

if [ $? -eq 0 ]; then
    echo "Compilation successful. Uploading to $PORT..."
    arduino-cli upload --fqbn $FQBN --port $PORT $SKETCH_DIR
    echo "Upload complete. Open your serial monitor at 115200 baud."
else
    echo "Compilation failed. Aborting upload."
    exit 1
fi

Comandos de compilación/flasheo/ejecución

Para compilar y cargar el firmware manualmente (si decide no usar el script de shell proporcionado), siga este flujo de trabajo estandarizado utilizando Arduino CLI.

Referencia de comandos

Acción Comando
Actualizar índice arduino-cli core update-index
Instalar Core arduino-cli core install arduino:avr
Compilar arduino-cli compile --fqbn arduino:avr:uno servo_tester
Cargar arduino-cli upload --fqbn arduino:avr:uno --port <PORT> servo_tester
Monitorizar arduino-cli monitor --port <PORT> --config baudrate=115200

Flujo de trabajo

  1. Identifique su puerto: Conecte su Arduino UNO R3 a su computadora. Ejecute arduino-cli board list para encontrar el identificador del puerto (por ejemplo, /dev/ttyUSB0, /dev/ttyACM0 o COM3).
  2. Prepare el entorno: Ejecute los comandos de actualización e instalación del core para asegurarse de que su sistema tenga la última cadena de herramientas (toolchain) para ATmega328P.
  3. Compile el código: Ejecute el comando de compilación apuntando al directorio servo_tester. Verifique que no se devuelvan errores de sintaxis.
  4. Flashee el dispositivo: Ejecute el comando de carga (upload), reemplazando <PORT> con el puerto identificado en el paso 1.
  5. Monitorice la salida: Inicie el comando del monitor serie para observar la secuencia de arranque y la telemetría.

Validación paso a paso

Una vez que el código esté cargado y el monitor serie esté abierto, realice las siguientes comprobaciones agrupadas para validar el sistema.

  • Punto de control 1: Encendido e inicialización

    • Acción: Reinicie el Arduino.
    • Observación esperada: El monitor serie muestra «Initializing Servo Calibration Tester…» seguido de «System Ready. Mode: MANUAL». El servo debería moverse a una posición inicial de 90 grados.
    • Condición de éxito: La telemetría comienza a imprimirse cada 500ms mostrando «Mode: MANUAL».
  • Punto de control 2: Modo de barrido manual

    • Acción: Mientras está en modo MANUAL, gire el potenciómetro desde el extremo izquierdo hasta el extremo derecho.
    • Observación esperada: El brazo del servo gira suavemente junto con el movimiento del potenciómetro.
    • Condición de éxito: El monitor serie informa de ángulos que cambian suavemente de ~0° a ~180° y anchos de pulso que cambian de ~1000 µs a ~2000 µs.
  • Punto de control 3: Cambio de modo y centrado

    • Acción: Presione el botón pulsador una vez.
    • Observación esperada: El monitor serie muestra «>>> Mode Switched: CENTER (90 deg) <<<«. El servo se mueve inmediatamente a su punto central mecánico y se bloquea allí.
    • Condición de éxito: Girar el potenciómetro ahora no tiene efecto en el servo. La telemetría muestra exactamente 90 grados y 1500 µs.
  • Punto de control 4: Modo de barrido automático

    • Acción: Presione el botón pulsador por segunda vez.
    • Observación esperada: El monitor serie muestra «>>> Mode Switched: AUTO-SWEEP <<<«. El servo comienza a oscilar hacia adelante y hacia atrás automáticamente.
    • Condición de éxito: La telemetría muestra el ángulo incrementándose a 180, invirtiéndose, decrementándose a 0 y repitiéndose.
  • Punto de control 5: Volver al modo manual

    • Acción: Presione el botón pulsador por tercera vez.
    • Observación esperada: El sistema vuelve al modo MANUAL.
    • Condición de éxito: El servo se ajusta inmediatamente al ángulo dictado actualmente por la posición física del contacto (wiper) del potenciómetro.

Solución de problemas

Síntoma Causa probable Solución
El servo vibra erráticamente (jitters) Fuente de alimentación inestable o mala conexión a tierra. Asegúrese de que todos los pines de tierra (Arduino, Potenciómetro, Servo) estén conectados a un riel común. Revise los cables puente.
El Arduino se reinicia cuando el servo se mueve El servo consume demasiada corriente (caída de voltaje / brownout). El servo está bajo carga mecánica. Desconecte la carga o alimente el servo desde una fuente externa de 5V (compartiendo las tierras).
Se ignora la pulsación del botón / disparos dobles El retardo antirrebote (debounce delay) es demasiado corto o el cableado está suelto. Aumente debounceDelay en el código a 100ms. Verifique que el botón esté firmemente asentado en la protoboard.
El servo solo gira 90 grados en total Desajuste en el mapeo del ancho de pulso PWM. Cambie testServo.attach(SERVO_PIN, 1000, 2000) a testServo.attach(SERVO_PIN, 500, 2400) para que coincida con los límites específicos de su SG90.
Error de compilación: «Servo.h: No such file» Falta la biblioteca estándar. Ejecute arduino-cli lib install Servo para asegurarse de que la biblioteca estándar esté disponible para el compilador.

Mejoras

Una vez que el prototipo base esté funcionando, considere estas mejoras agrupadas:

Actualizaciones de hardware
* Fuente de alimentación externa: Agregue una fuente de alimentación dedicada de 5V y 2A (como un UBEC o una fuente de banco) a los rieles de alimentación de la protoboard para probar servos de engranajes metálicos de servicio pesado y alto torque sin provocar caídas de voltaje (brownout) en el Arduino.
* Pantalla OLED: Agregue una pantalla OLED I2C de 0.96″ para mostrar el ángulo actual y el ancho de pulso directamente en el dispositivo, eliminando la necesidad de una computadora conectada y un monitor serie.

Mejoras de software
* Ajuste personalizado del ancho de pulso: Agregue un cuarto modo que utilice el potenciómetro para ajustar finamente los límites mínimo y máximo en microsegundos, permitiéndole mapear los puntos finales físicos exactos de servos de marcas genéricas.
* Modo de prueba de velocidad: Implemente un modo que ordene saltos instantáneos de 0° a 180° y mida el tiempo de la respuesta física, ayudando a verificar las especificaciones de velocidad del servo.

Lista de verificación

  • [ ] Arduino CLI está instalado y el core AVR está actualizado.
  • [ ] Los componentes están cableados de acuerdo con la tabla de Configuración (verificando la tierra común).
  • [ ] El botón pulsador está cableado correctamente para utilizar la resistencia pull-up interna.
  • [ ] servo_tester.ino se ha guardado en el directorio correcto.
  • [ ] El sketch se compila correctamente utilizando arduino-cli compile.
  • [ ] El firmware se carga correctamente en el puerto serie adecuado.
  • [ ] El monitor serie muestra la secuencia de inicialización a 115200 baudios.
  • [ ] El servo responde suavemente al potenciómetro en modo MANUAL.
  • [ ] El botón pulsador alterna con éxito a través de los tres modos operativos.

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.

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:


Caso práctico: cerradura RFID con Arduino UNO R3

Caso práctico: cerradura RFID con Arduino UNO R3 — hero

Objetivo y caso de uso

Qué construirás: Un prototipo básico de cerradura de puerta controlada por RFID usando un Arduino UNO R3, lector RFID MFRC522 y relé de 1 canal. Cuando se detecta una tarjeta o llavero autorizado, el Arduino valida el UID y energiza el relé durante 2–5 segundos para accionar una cerradura eléctrica de pestillo, una interfaz de cerradura magnética o un circuito de cerradura de bajo voltaje.

Por qué importa / Casos de uso

  • Prototipo de acceso para pequeño taller o gabinete: Controla el acceso a un gabinete de herramientas, cajón de laboratorio o recinto de demostración para que solo las etiquetas RFID conocidas puedan desbloquearlo.
  • Demostrador conceptual de sistema de entrada: Practica el mismo flujo usado en sistemas más grandes: lectura de credencial, coincidencia de UID, desbloqueo temporizado y control de cerradura basado en relé.
  • Práctica de integración electromecánica: Combina detección RFID basada en SPI con una salida de relé, avanzando más allá de proyectos solo con LED hacia conmutación real de actuadores.
  • Resolución de problemas y auditoría con monitor serie: Imprime los UID detectados y eventos de permitir/denegar por serie a 9600 baudios para depuración rápida y registros básicos de acceso durante las pruebas.

Resultado esperado

  • Una tarjeta RFID se detecta y compara normalmente en menos de 200 ms desde su presentación hasta la decisión.
  • El relé se activa durante una ventana de desbloqueo configurable, comúnmente de 3 segundos, y luego vuelve automáticamente al estado bloqueado.
  • Las etiquetas no autorizadas se rechazan de inmediato, mostrando el UID en el Monitor Serie para registro o diagnóstico.
  • El prototipo funciona cómodamente en el ATmega328P de 16 MHz del UNO con baja carga de CPU y sin requerimiento de GPU.

Audiencia: principiantes en Arduino, estudiantes y personas aprendiendo sistemas embebidos/electrónica que construyen demostraciones de control de acceso; Nivel: principiante a intermedio

Arquitectura/flujo: El MFRC522 lee el UID de la etiqueta por SPI → Arduino UNO compara el UID con una lista autorizada en el código → si coincide, una salida digital activa el relé de 1 canal → el relé acciona la interfaz de cerradura durante un intervalo temporizado → el Monitor Serie informa el estado de permitir/denegar y los valores UID.

Nota educativa de validación

Antes de la publicación, este caso pasó la compuerta de validación automatizada de Prometeo con estado PASS. El validador comprobó los bloques de código, la estructura del artículo, los comandos seguros para copiar/pegar y la coherencia con el catálogo de dispositivos compatibles.

Evidencia de validación publicada

  • Resultado automático: PASS.
  • Estructura analizada: 3 secciones, 1 tablas y 24 bloques de código detectados antes de la publicación.
  • Código comprobado: 7 comprobaciones estáticas de C/C++, 12 comprobaciones de Bash/copiar-pegar.
  • Catálogo compatible: el texto del artículo fue verificado contra los perfiles de dispositivos con capacidad de validación de Prometeo, y las pilas no compatibles bloquean la publicación.
  • Hallazgos del informe: sin hallazgos bloqueantes.

Esta validación confirma la compatibilidad de sintaxis y herramientas del material publicado, pero no sustituye las pruebas físicas en tu hardware, cableado y entorno de ejecución exactos.

Nota educativa de seguridad

Este prototipo es para aprendizaje educativo de control de acceso de bajo voltaje. No es un producto de seguridad certificado y no debe considerarse la única protección para hogares, negocios, áreas peligrosas o salidas de seguridad vital.

Límites y precauciones importantes:

  • No conmutas tensión de red directamente a menos que estés capacitado y uses hardware y prácticas de encapsulado correctamente dimensionados.
  • Para uso estudiantil, valida primero el relé con una carga segura de bajo voltaje.
  • La autorización RFID basada solo en UID es una seguridad débil.
  • Muchas etiquetas RFID de bajo costo pueden clonarse, y verificar solo el UID no es lo bastante seguro para protección seria.
  • Importa el voltaje del módulo MFRC522.
  • El lector es un dispositivo de 3.3 V. Revisa la tolerancia lógica de tu módulo específico antes de conectarlo a líneas SPI de Arduino de 5 V.
  • Las cerraduras eléctricas reales pueden requerir alimentación y protección separadas.
  • Sigue los requisitos de corriente, voltaje y supresión del fabricante de la cerradura.
  • No uses este proyecto para puertas de evacuación de emergencia ni cerraduras críticas para la seguridad.
  • Un fallo de software, fallo del relé, pérdida de alimentación o defecto de cableado podría impedir el funcionamiento esperado.
  • Monta y aísla el cableado correctamente.
  • Los cables sueltos pueden causar reinicios, activaciones falsas o módulos dañados.

Diagrama de bloques conceptual

Vista de alto nivel: qué entra, qué procesa cada bloque y qué sale del sistema.

Arquitectura funcional

El MFRC522 lee el UID de la etiqueta por SPI

Arduino UNO compara el UID con una lista…

si coincide, una salida digital activa el…

el relé acciona la interfaz de cerradura…

el Monitor Serie informa el estado de per…

Flujo conceptual de señales y responsabilidades entre bloques del dispositivo.

Ruta de validación

Sketch

arduino-cli compile

Upload

Prueba funcional

Resumen conceptual de las herramientas usadas para comprobar el material publicado.

Requisitos previos

Antes de comenzar, deberías sentirte cómodo con:

  • Subir un sketch a un Arduino UNO
  • Abrir el Monitor Serie
  • Hacer conexiones simples con cables jumper en una protoboard o encabezados de módulos
  • Entender que un relé es un interruptor controlado eléctricamente

No necesitas experiencia previa con RFID. Este tutorial explica lo mínimo necesario para hacer un prototipo de control de acceso utilizable.

Materiales

Usa la familia y modelo exactos de dispositivo solicitados:

  • Arduino UNO R3 (ATmega328P) + módulo RFID MFRC522 + módulo de relé de 1 canal

Lista completa de piezas recomendada:

  • 1 x Arduino UNO R3 (ATmega328P)
  • 1 x módulo lector RFID MFRC522 con tarjeta RFID o llavero compatible
  • 1 x módulo de relé de 1 canal
  • Prefiere un módulo con controlador por transistor y optoaislamiento o protección flyback integrada
  • El lado de la bobina normalmente se alimenta con 5 V
  • Cables jumper
  • Cable USB para Arduino UNO
  • Protoboard o superficie de cableado estable
  • Opcional pero muy recomendable para pruebas visibles:
  • 1 x LED
  • 1 x resistencia de 220 ohm
  • Carga educativa opcional del lado de la cerradura:
  • Lámpara de bajo voltaje, indicador LED o pequeña carga DC controlada a través de los contactos del relé
  • Si más adelante conectas una cerradura eléctrica de pestillo real o una interfaz de cerradura magnética, usa la fuente de alimentación externa recomendada por el fabricante de la cerradura

Configuración/Conexión

Cómo funciona el sistema

El MFRC522 lee el UID de una etiqueta RFID cercana por SPI.
El Arduino compara ese UID con una pequeña lista de UID autorizados almacenada en el sketch.
Si el UID coincide, el Arduino acciona la entrada del módulo de relé durante un breve tiempo.
Los contactos del relé pueden entonces conmutar un circuito de cerradura separado o una carga educativa de prueba.

Nota importante de voltaje antes del cableado

El lector MFRC522 es un dispositivo de 3.3 V. La mayoría de las placas breakout comunes exponen pines etiquetados como SDA, SCK, MOSI, MISO, RST, 3.3V y GND. En tutoriales típicos para aficionados, el módulo se alimenta desde 3.3 V, mientras que las líneas SPI se conectan directamente al UNO. Muchos estudiantes lo hacen con éxito en la práctica, pero eléctricamente el UNO entrega lógica de 5 V. Si tu placa MFRC522 no tolera 5 V en los pines lógicos, usa adaptación de nivel adecuada. Para una construcción educativa básica, sigue el patrón de uso común del módulo solo si se sabe que tu módulo específico funciona así.

Asignación de pines

Usa el cableado SPI común de MFRC522 a UNO y un pin digital para el relé.

Módulo/Señal Pin de Arduino UNO R3 Notas
MFRC522 SDA / SS D10 Selección de esclavo SPI
MFRC522 SCK D13 Reloj SPI
MFRC522 MOSI D11 SPI MOSI
MFRC522 MISO D12 SPI MISO
MFRC522 RST D9 Pin de reinicio del lector
MFRC522 3.3V 3.3V No uses 5V para alimentar el lector
MFRC522 GND GND Tierra común
Relay IN D7 Señal de control del relé
Relay VCC 5V Alimentación típica del módulo de relé
Relay GND GND Tierra común
Ánodo del LED opcional mediante resistencia de 220 ohm D6 Indicador de estado
Cátodo del LED opcional GND Retorno del LED

Pasos de conexión solo texto

  1. Apaga el Arduino antes de hacer o cambiar el cableado.
  2. Conecta el MFRC522:
  3. MFRC522 3.3V -> UNO 3.3V
  4. MFRC522 GND -> UNO GND
  5. MFRC522 SDA/SS -> UNO D10
  6. MFRC522 SCK -> UNO D13
  7. MFRC522 MOSI -> UNO D11
  8. MFRC522 MISO -> UNO D12
  9. MFRC522 RST -> UNO D9
  10. Conecta el módulo de relé:
  11. Relay VCC -> UNO 5V
  12. Relay GND -> UNO GND
  13. Relay IN -> UNO D7
  14. LED de estado visible opcional:
  15. UNO D6 -> resistencia de 220 ohm
  16. Resistencia -> ánodo del LED
  17. Cátodo del LED -> GND
  18. Si pruebas los contactos del relé con una carga segura de bajo voltaje:
  19. Usa COM y NO del relé para “normalmente apagado, encendido solo cuando se desbloquea”
  20. Cablea la ruta de alimentación de bajo voltaje a través de COM y NO
  21. Mantén el circuito conmutado eléctricamente adecuado para las especificaciones del relé y el diseño del módulo

Concepto de contactos del relé

El módulo de relé tiene dos lados:

  • Lado de control
  • VCC, GND, IN
  • Conectado al Arduino
  • Lado de conmutación
  • COM, NO, NC
  • Conectado al circuito de cerradura o carga de prueba

Para una acción de desbloqueo de puerta, COM + NO suele ser lo más simple:
– En reposo: circuito abierto
– Tarjeta autorizada: el relé conecta COM a NO durante unos segundos

Código validado

Sketch de Arduino: rfid_door_lock_relay.ino

Vista pública parcial del archivo validado. El código completo se muestra a miembros y en PDF/Print.

#include <SPI.h>
#include <MFRC522.h>

static const uint8_t SS_PIN = 10;
static const uint8_t RST_PIN = 9;
static const uint8_t RELAY_PIN = 7;
static const uint8_t STATUS_LED_PIN = 6;

// Many relay modules are ACTIVE LOW.
// Set to true if your relay turns on when the Arduino pin goes LOW.
// Set to false if your relay turns on when the Arduino pin goes HIGH.
static const bool RELAY_ACTIVE_LOW = true;

// Unlock timing in milliseconds
static const unsigned long UNLOCK_TIME_MS = 3000;

// Example authorized UIDs.
// Replace these with the UIDs printed by your own cards during enrollment/testing.
const byte AUTHORIZED_UIDS[][4] = {
  {0xDE, 0xAD, 0xBE, 0xEF},
  {0x12, 0x34, 0x56, 0x78}
};
const size_t AUTHORIZED_UID_COUNT = sizeof(AUTHORIZED_UIDS) / sizeof(AUTHORIZED_UIDS[0]);

MFRC522 mfrc522(SS_PIN, RST_PIN);

bool relayActive = false;
unsigned long relayActivatedAt = 0;
String lastUidString = "";
unsigned long lastScanAt = 0;

// Ignore repeated reads of the same card within this time window
static const unsigned long SAME_CARD_DEBOUNCE_MS = 1500;

void setRelay(bool on) {
  relayActive = on;

  if (RELAY_ACTIVE_LOW) {
    digitalWrite(RELAY_PIN, on ? LOW : HIGH);
  } else {
    digitalWrite(RELAY_PIN, on ? HIGH : LOW);
  }

  digitalWrite(STATUS_LED_PIN, on ? HIGH : LOW);
}

void printUid(const MFRC522::Uid *uid) {
  for (byte i = 0; i < uid->size; i++) {
    if (uid->uidByte[i] < 0x10) {
      Serial.print("0");
    }
    Serial.print(uid->uidByte[i], HEX);
    if (i < uid->size - 1) {
      Serial.print(":");
    }
  }
}

String uidToString(const MFRC522::Uid *uid) {
  String s = "";
  for (byte i = 0; i < uid->size; i++) {
    if (uid->uidByte[i] < 0x10) {
      s += "0";
    }
    s += String(uid->uidByte[i], HEX);
    if (i < uid->size - 1) {
      s += ":";
    }
  }
  s.toUpperCase();
  return s;
}

bool isAuthorized(const MFRC522::Uid *uid) {
  // This basic example checks only 4-byte UIDs against a fixed list.
  if (uid->size != 4) {
    return false;
  }

  for (size_t i = 0; i < AUTHORIZED_UID_COUNT; i++) {
    bool match = true;
    for (byte j = 0; j < 4; j++) {
      if (uid->uidByte[j] != AUTHORIZED_UIDS[i][j]) {
        match = false;
        break;
      }
// ... continúa para miembros en el código completo validado ...

🔒 Parte del código validado es premium. Con el pase de 7 días o la suscripción mensual podrás consultar el archivo completo validado.

#include <SPI.h>
#include <MFRC522.h>

static const uint8_t SS_PIN = 10;
static const uint8_t RST_PIN = 9;
static const uint8_t RELAY_PIN = 7;
static const uint8_t STATUS_LED_PIN = 6;

// Many relay modules are ACTIVE LOW.
// Set to true if your relay turns on when the Arduino pin goes LOW.
// Set to false if your relay turns on when the Arduino pin goes HIGH.
static const bool RELAY_ACTIVE_LOW = true;

// Unlock timing in milliseconds
static const unsigned long UNLOCK_TIME_MS = 3000;

// Example authorized UIDs.
// Replace these with the UIDs printed by your own cards during enrollment/testing.
const byte AUTHORIZED_UIDS[][4] = {
  {0xDE, 0xAD, 0xBE, 0xEF},
  {0x12, 0x34, 0x56, 0x78}
};
const size_t AUTHORIZED_UID_COUNT = sizeof(AUTHORIZED_UIDS) / sizeof(AUTHORIZED_UIDS[0]);

MFRC522 mfrc522(SS_PIN, RST_PIN);

bool relayActive = false;
unsigned long relayActivatedAt = 0;
String lastUidString = "";
unsigned long lastScanAt = 0;

// Ignore repeated reads of the same card within this time window
static const unsigned long SAME_CARD_DEBOUNCE_MS = 1500;

void setRelay(bool on) {
  relayActive = on;

  if (RELAY_ACTIVE_LOW) {
    digitalWrite(RELAY_PIN, on ? LOW : HIGH);
  } else {
    digitalWrite(RELAY_PIN, on ? HIGH : LOW);
  }

  digitalWrite(STATUS_LED_PIN, on ? HIGH : LOW);
}

void printUid(const MFRC522::Uid *uid) {
  for (byte i = 0; i < uid->size; i++) {
    if (uid->uidByte[i] < 0x10) {
      Serial.print("0");
    }
    Serial.print(uid->uidByte[i], HEX);
    if (i < uid->size - 1) {
      Serial.print(":");
    }
  }
}

String uidToString(const MFRC522::Uid *uid) {
  String s = "";
  for (byte i = 0; i < uid->size; i++) {
    if (uid->uidByte[i] < 0x10) {
      s += "0";
    }
    s += String(uid->uidByte[i], HEX);
    if (i < uid->size - 1) {
      s += ":";
    }
  }
  s.toUpperCase();
  return s;
}

bool isAuthorized(const MFRC522::Uid *uid) {
  // This basic example checks only 4-byte UIDs against a fixed list.
  if (uid->size != 4) {
    return false;
  }

  for (size_t i = 0; i < AUTHORIZED_UID_COUNT; i++) {
    bool match = true;
    for (byte j = 0; j < 4; j++) {
      if (uid->uidByte[j] != AUTHORIZED_UIDS[i][j]) {
        match = false;
        break;
      }
    }
    if (match) {
      return true;
    }
  }
  return false;
}

void handleAuthorizedCard() {
  setRelay(true);
  relayActivatedAt = millis();
  Serial.print("ACCESS GRANTED - relay ON for ");
  Serial.print(UNLOCK_TIME_MS);
  Serial.println(" ms");
}

void setup() {
  pinMode(RELAY_PIN, OUTPUT);
  pinMode(STATUS_LED_PIN, OUTPUT);

  setRelay(false);

  Serial.begin(9600);
  while (!Serial) {
    ; // UNO usually continues immediately
  }

  SPI.begin();
  mfrc522.PCD_Init();

  delay(50);

  Serial.println();
  Serial.println("RFID Door Lock Relay Prototype");
  Serial.println("Present a card/tag to the MFRC522 reader.");
  Serial.println("Known 4-byte UIDs will activate the relay.");
}

void loop() {
  // Turn relay off after unlock period
  if (relayActive && (millis() - relayActivatedAt >= UNLOCK_TIME_MS)) {
    setRelay(false);
    Serial.println("Relay OFF - lock returned to idle state");
  }

  // No new card
  if (!mfrc522.PICC_IsNewCardPresent()) {
    return;
  }

  // Unable to read card serial
  if (!mfrc522.PICC_ReadCardSerial()) {
    return;
  }

  String currentUid = uidToString(&mfrc522.uid);
  unsigned long now = millis();

  // Debounce repeated scans of the same card
  if (currentUid == lastUidString && (now - lastScanAt < SAME_CARD_DEBOUNCE_MS)) {
    mfrc522.PICC_HaltA();
    mfrc522.PCD_StopCrypto1();
    return;
  }

  lastUidString = currentUid;
  lastScanAt = now;

  Serial.print("Card detected. UID=");
  printUid(&mfrc522.uid);
  Serial.println();

  if (isAuthorized(&mfrc522.uid)) {
    handleAuthorizedCard();
  } else {
    Serial.println("ACCESS DENIED - unauthorized card");
  }

  mfrc522.PICC_HaltA();
  mfrc522.PCD_StopCrypto1();
}

Instalación de dependencia de biblioteca

La biblioteca MFRC522 no forma parte del núcleo estándar integrado de Arduino, así que instálala explícitamente con Arduino CLI.

arduino-cli lib install "MFRC522"

Comandos de compilación/flash/ejecución

Los siguientes comandos coinciden con el flujo de trabajo requerido de Arduino CLI para Arduino UNO R3.

1) Actualizar el índice de placas

arduino-cli core update-index

2) Instalar el núcleo AVR

arduino-cli core install arduino:avr

3) Instalar la biblioteca RFID

arduino-cli lib install "MFRC522"

4) Crear la carpeta del sketch

Directorio de ejemplo:

mkdir -p rfid-door-lock-relay

Guarda el sketch como:

rfid-door-lock-relay/rfid_door_lock_relay.ino

5) Compilar

Ejecuta esto desde el directorio padre de la carpeta del sketch, o especifica la ruta completa.

arduino-cli compile --fqbn arduino:avr:uno rfid-door-lock-relay

6) Encontrar tu puerto serie

En Linux:

arduino-cli board list

En Windows, el puerto puede aparecer como COM3, COM4, etc.
En Linux, algo como /dev/ttyACM0 o /dev/ttyUSB0.
En macOS, algo como /dev/cu.usbmodem....

7) Subir

Reemplaza <PORT> por tu puerto detectado real.

arduino-cli upload --fqbn arduino:avr:uno --port <PORT> rfid-door-lock-relay

8) Abrir el monitor serie

arduino-cli monitor --port <PORT> --config baudrate=9600

Validación paso a paso

Esta sección te ayuda a demostrar que el prototipo se comporta como se pretende.

1. Validar la etapa de compilación

Objetivo:
– Confirmar que la sintaxis del sketch, la placa objetivo y la biblioteca instalada son correctas.

Procedimiento:
1. Ejecuta:
bash
arduino-cli compile --fqbn arduino:avr:uno rfid-door-lock-relay

2. Resultado esperado:
– La compilación se completa correctamente.
– No hay errores de biblioteca faltante para MFRC522.h.

Qué valida esto:
– El código es estructuralmente válido para Arduino UNO.
– La dependencia de biblioteca está correctamente instalada.

Qué no valida esto:
– Cableado
– Legibilidad de la tarjeta
– Polaridad del relé
– Compatibilidad de la cerradura física

2. Validar el comportamiento de arranque en reposo

Objetivo:
– Confirmar que la placa arranca y el relé comienza en el estado bloqueado/en reposo.

Procedimiento:
1. Sube el sketch.
2. Abre el monitor serie a 9600 baudios.
3. Reinicia la placa si es necesario.

Patrón de registro esperado:

RFID Door Lock Relay Prototype
Present a card/tag to the MFRC522 reader.
Known 4-byte UIDs will activate the relay.

Comportamiento de hardware esperado:
– El relé debe estar en reposo, no energizado continuamente.
– El LED opcional debe estar apagado.

Si el relé está ENCENDIDO en reposo:
– Tu módulo puede usar lógica de entrada opuesta.
– Cambia:
cpp
static const bool RELAY_ACTIVE_LOW = true;

a:
cpp
static const bool RELAY_ACTIVE_LOW = false;

– Vuelve a compilar y subir.

3. Descubrir el UID de tu tarjeta

Objetivo:
– Leer el UID real de tu tarjeta RFID o llavero.

Procedimiento:
1. Presenta una tarjeta al lector.
2. Observa el monitor serie.

Ejemplo de salida esperada:

Card detected. UID=93:4A:1C:7F
ACCESS DENIED - unauthorized card

Registra el UID exactamente.

Notas:
– El ejemplo de código proporcionado autoriza solo UID de 4 bytes.
– Muchas tarjetas MIFARE comunes funcionan así, pero algunas etiquetas pueden tener longitudes de UID diferentes.
– Para un proyecto básico, usa una tarjeta con UID de 4 bytes si es posible.

4. Agregar tu UID autorizado real

Objetivo:
– Hacer que tu tarjeta desbloquee el relé.

Procedimiento:
1. Edita esta sección:
cpp
const byte AUTHORIZED_UIDS[][4] = {
{0xDE, 0xAD, 0xBE, 0xEF},
{0x12, 0x34, 0x56, 0x78}
};

2. Reemplaza una línea con los bytes UID reales.
Ejemplo, si el monitor mostró 93:4A:1C:7F:
cpp
const byte AUTHORIZED_UIDS[][4] = {
{0x93, 0x4A, 0x1C, 0x7F}
};

3. Vuelve a compilar y subir.

5. Validar acceso exitoso

Objetivo:
– Confirmar que una tarjeta autorizada activa el relé durante la duración configurada.

Procedimiento:
1. Presenta la tarjeta autorizada una vez.
2. Observa la salida serie.
3. Escucha el clic del relé y observa el LED opcional.

Salida esperada:

Card detected. UID=93:4A:1C:7F
ACCESS GRANTED - relay ON for 3000 ms
Relay OFF - lock returned to idle state

Comportamiento medible esperado:
– El relé se activa una vez
– El relé permanece activo alrededor de 3 segundos
– El relé vuelve al reposo automáticamente

6. Validar acceso denegado

Objetivo:
– Asegurarse de que una tarjeta no listada no desbloquee.

Procedimiento:
1. Presenta una tarjeta RFID o llavero diferente.
2. Observa el monitor serie.

Salida esperada:

Card detected. UID=11:22:33:44
ACCESS DENIED - unauthorized card

Comportamiento de hardware esperado:
– Sin acción de desbloqueo del relé
– El LED opcional permanece apagado

7. Validar el manejo de lecturas repetidas

Objetivo:
– Confirmar que la misma tarjeta sostenida cerca del lector no vuelva a dispararse rápidamente.

Procedimiento:
1. Mantén una tarjeta autorizada sobre el lector continuamente.
2. Observa el comportamiento durante unos segundos.

Resultado esperado:
– Debe ocurrir un evento de desbloqueo.
– La ventana de antirrebote evita el re-disparo repetido inmediato de la misma lectura.

Solución de problemas

El lector RFID no detecta ninguna tarjeta

Comprueba:

  • MFRC522 alimentado desde 3.3 V, no 5 V
  • Los pines SPI son exactamente:
  • D10 -> SDA/SS
  • D11 -> MOSI
  • D12 -> MISO
  • D13 -> SCK
  • D9 -> RST
  • La tarjeta es compatible con la frecuencia y el tipo del lector MFRC522
  • Las tierras son comunes entre todos los módulos

Síntoma:
– No hay mensajes de UID en absoluto en el Monitor Serie

Causas probables:
– Cableado SPI incorrecto
– Biblioteca faltante poco probable si el código ya se subió
– Problema de alimentación del lector
– Mal contacto de jumper

El relé permanece encendido todo el tiempo

Causa probable:
– La polaridad del módulo de relé es opuesta a la configuración de tu código

Corrección:
– Cambia:
cpp
static const bool RELAY_ACTIVE_LOW = true;

a:
cpp
static const bool RELAY_ACTIVE_LOW = false;

– Vuelve a compilar y subir

La tarjeta autorizada sigue apareciendo como denegada

Comprueba:

  • Bytes UID copiados correctamente
  • Los valores hexadecimales incluyen 0x
  • La tarjeta tiene UID de 4 bytes
  • Volviste a subir después de editar

La salida serie muestra el UID en minúsculas o con estilo mixto

Eso está bien siempre que los valores de bytes sean correctos. La lógica de coincidencia usa bytes sin procesar, no el formato impreso.

El relé hace clic pero tu cerradura o carga de prueba no se activa

Comprueba el lado de conmutación:

  • ¿Estás usando COM y NO?
  • ¿Está presente la fuente externa de bajo voltaje?
  • ¿Está completo el circuito de carga a través de los contactos del relé?
  • ¿Está la carga dentro de la corriente/voltaje nominal del relé?

El Arduino se reinicia cuando conmuta el relé

Posibles razones:

  • La alimentación USB es débil
  • El módulo de relé causa ruido o caída de suministro
  • El cableado está demasiado suelto o es demasiado largo

Prueba:

  • Cables más cortos
  • Mejor fuente de 5 V para el módulo de relé si corresponde
  • Asegúrate de que la tierra común sea sólida
  • Prueba primero solo con el módulo de relé, luego agrega la carga del lado de la cerradura

Mejoras

Una vez que el prototipo básico funcione, estos son pasos siguientes útiles:

Agregar LED de estado separados

Usa:
– LED verde para acceso concedido
– LED rojo para acceso denegado

Esto mejora la usabilidad sin necesidad del Monitor Serie.

Agregar un zumbador

Un pitido corto para acceso válido y un patrón diferente para acceso denegado hace que el sistema se sienta más realista.

Agregar modo de registro

En lugar de codificar los UID directamente, podrías:
– pulsar un botón,
– escanear una tarjeta maestra,
– almacenar nuevos UID en EEPROM.

Eso haría el prototipo más práctico.

Agregar un sensor de puerta

Un interruptor magnético reed puede detectar si la puerta realmente se abrió o permaneció abierta demasiado tiempo.

Mejorar la lógica de seguridad

Este proyecto verifica solo el UID de la tarjeta. Para un sistema más realista, podrías explorar:
– conceptos de autenticación por sector,
– tarjetas maestras/admin,
– ideas de anti-passback,
– registro de eventos en almacenamiento externo.

Agregar un temporizador de bloqueo

Después de varios intentos denegados, el sistema podría ignorar escaneos durante 30 segundos y parpadear un LED de advertencia.

Lista de verificación final

Usa esta lista de verificación antes de dar el proyecto por terminado:

  • [ ] Arduino UNO R3 está cableado correctamente
  • [ ] MFRC522 está alimentado desde 3.3 V
  • [ ] El módulo de relé está cableado a D7, 5V y GND
  • [ ] Existe tierra común entre Arduino, módulo RFID y módulo de relé
  • [ ] El sketch está guardado como rfid_door_lock_relay.ino
  • [ ] La biblioteca MFRC522 está instalada con Arduino CLI
  • [ ] El proyecto compila con:
    bash
    arduino-cli compile --fqbn arduino:avr:uno rfid-door-lock-relay
  • [ ] El proyecto se sube con:
    bash
    arduino-cli upload --fqbn arduino:avr:uno --port <PORT> rfid-door-lock-relay
  • [ ] El Monitor Serie a 9600 baudios muestra el texto de inicio
  • [ ] Una tarjeta desconocida produce ACCESS DENIED
  • [ ] Una tarjeta autorizada produce ACCESS GRANTED
  • [ ] El relé se activa durante aproximadamente 3000 ms
  • [ ] El relé vuelve al reposo automáticamente
  • [ ] La carga de prueba de bajo voltaje conmuta correctamente a través de los contactos del relé
  • [ ] Entiendes que este es un prototipo educativo, no un sistema de seguridad certificado

Con eso, tienes un prototipo práctico de relé de cerradura de puerta RFID que un principiante puede realmente montar, probar y ampliar hacia un proyecto de control de acceso más realista.

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: ¿Qué componentes principales se utilizan para construir el prototipo de cerradura?




Pregunta 2: ¿Qué acción realiza el Arduino cuando detecta una tarjeta o llavero autorizado?




Pregunta 3: ¿Cuál es uno de los casos de uso mencionados para este prototipo?




Pregunta 4: ¿Qué protocolo de comunicación utiliza el lector RFID en este proyecto?




Pregunta 5: ¿Qué información se imprime en el monitor serie según el texto?




Pregunta 6: ¿Qué tipo de cerraduras se pueden accionar con este sistema según el texto?




Pregunta 7: ¿Durante cuánto tiempo se energiza el relé al detectar una tarjeta autorizada?




Pregunta 8: ¿Qué tipo de salida se utiliza para controlar la cerradura en este proyecto?




Pregunta 9: ¿Qué modelo exacto de placa Arduino se menciona en el texto para construir el prototipo?




Pregunta 10: ¿Qué avance práctico representa este proyecto en comparación con proyectos básicos?




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: