Caso práctico: fichaje RFID en SD con Arduino UNO

Caso práctico: fichaje RFID en SD con Arduino UNO — hero

Objetivo y caso de uso

Qué construirás: Un prototipo de hardware independiente que lee etiquetas RFID pasivas, obtiene la hora actual de un reloj de tiempo real (RTC) de precisión y registra el evento (UID y marca de tiempo) en un archivo CSV en una tarjeta microSD.

Por qué es importante / Casos de uso

  • Seguimiento de asistencia de estudiantes: Automatiza el registro en el aula mediante toques de tarjetas de identificación, eliminando pasar lista manualmente.
  • Registro de acceso a equipos de laboratorio: Proporciona un registro de auditoría sin conexión de quién usó exactamente una máquina específica y a qué hora.
  • Reloj de control de empleados: Actúa como un sistema básico de entrada/salida para pequeños talleres donde no hay conectividad de red disponible.
  • Auditoría de seguridad: Demuestra los principios fundamentales de los sistemas de control de acceso físico registrando los intentos de entrada en una puerta o portón.

Resultado esperado

  • Inicialización exitosa de los componentes del bus SPI compartido (microSD y RFID) y los componentes I2C (RTC).
  • Salida del monitor serie mostrando los UID de las tarjetas detectadas y las marcas de tiempo en tiempo real (latencia de lectura < 100ms).
  • Creación de un archivo ATTEND.CSV en la tarjeta microSD que contenga entradas de registro separadas por comas y con el formato adecuado.

Audiencia: Desarrolladores de sistemas embebidos y makers; Nivel: Intermedio

Arquitectura/flujo: Toque de etiqueta RFID → Lector RFID SPI → Microcontrolador → Obtención de marca de tiempo RTC I2C → Adición a CSV en microSD SPI

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, 5 tablas y 2 bloques de código detectados antes de publicar.
  • Código comprobado: 2 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 construye un prototipo educativo destinado al aprendizaje de la integración de hardware, el uso compartido del bus SPI y el registro de datos. No es un sistema de control de acceso seguro. Las etiquetas MIFARE Classic utilizadas comúnmente con el MFRC522 tienen vulnerabilidades criptográficas bien documentadas y

Diagrama de bloques conceptual

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

Arquitectura funcional

Toque de etiqueta RFID

Lector RFID SPI

Microcontrolador

Obtención de marca de tiempo RTC I2C

Adición a CSV en microSD SPI

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, necesitas:
* Comprensión básica de la programación en C++ y la estructura de un sketch de Arduino (setup() y loop()).
* Familiaridad con la comunicación serie y la lectura de las salidas del monitor serie.
* Una computadora con el arduino-cli (Interfaz de línea de comandos de Arduino) instalado y agregado al path de tu sistema.
* Una tarjeta microSD (32GB o menor) formateada en FAT32.
* Un cable USB Tipo-A a Tipo-B para programar y alimentar el Arduino UNO.

Materiales

  • Arduino UNO R3 (ATmega328P) + módulo RFID MFRC522 + módulo microSD SPI + RTC DS3231 (Hardware objetivo exacto).
  • Etiquetas o tarjetas RFID pasivas (13.56 MHz, compatibles con MFRC522, típicamente MIFARE Classic).
  • Protoboard y cables puente premium macho-macho y macho-hembra.
  • Tarjeta microSD (formateada en FAT32).

Configuración/Conexión

Este proyecto requiere un cableado cuidadoso porque dos módulos diferentes (el MFRC522 y el módulo microSD) deben compartir el único bus SPI de hardware del Arduino. El bus SPI utiliza líneas compartidas para datos (MOSI, MISO) y reloj (SCK), pero requiere pines de selección de chip (CS) únicos para cada dispositivo. El RTC DS3231 utiliza el bus I2C, que opera en pines separados.

Advertencia de distribución de energía

  • MFRC522: Debe ser alimentado con 3.3V. Aplicar 5V al pin VCC del MFRC522 lo dañará.
  • Módulo microSD: La mayoría de los módulos microSD estándar para Arduino tienen un regulador de voltaje y un cambiador de nivel integrados. Estos deben ser alimentados con 5V.
  • DS3231: Puede operar de manera segura a 5V.

Tablas de cableado

1. Módulo RFID MFRC522 (Bus SPI 1 – Lógica 3.3V)

Pin MFRC522 Pin Arduino UNO R3 Función / Nota
3.3V 3.3V CRÍTICO: No conectar a 5V
RST Pin 9 Control de reinicio
GND GND Tierra común
IRQ Sin conectar No se usa en esta implementación de sondeo
MISO Pin 12 Master In Slave Out (SPI compartido)
MOSI Pin 11 Master Out Slave In (SPI compartido)
SCK Pin 13 Reloj serie (SPI compartido)
SDA (CS) Pin 10 Selección de chip para RFID

2. Módulo de Tarjeta MicroSD (Bus SPI 2 – Lógica 5V)

Pin MicroSD Pin Arduino UNO R3 Función / Nota
VCC 5V Alimentación para el regulador integrado
GND GND Tierra común
MISO Pin 12 Master In Slave Out (SPI compartido)
MOSI Pin 11 Master Out Slave In (SPI compartido)
SCK Pin 13 Reloj serie (SPI compartido)
CS Pin 4 Selección de chip para tarjeta SD

3. Módulo RTC DS3231 (Bus I2C)

Pin DS3231 Pin Arduino UNO R3 Función / Nota
VCC 5V Alimentación
GND GND Tierra común
SDA Pin A4 Datos I2C
SCL Pin A5 Reloj I2C

Código validado

El proyecto utiliza dos archivos de código separados. El primero es un sketch de utilidad para configurar la fecha y hora actuales en tu RTC DS3231. El segundo es la aplicación principal de registro de asistencia.

Sketch de utilidad: set_rtc_time.ino

Ejecuta este sketch una vez para programar el RTC con la hora de compilación actual de tu computadora.

/*
 * Utility: Set DS3231 RTC Time
 * This sketch sets the RTC to the date & time the sketch was compiled.
 */

#include <Wire.h>
#include <RTClib.h>

RTC_DS3231 rtc;

void setup() {
  Serial.begin(9600);
  while (!Serial) { delay(10); } // Wait for serial console

  Serial.println("Initializing RTC...");

  if (!rtc.begin()) {
    Serial.println("Couldn't find RTC. Check wiring.");
    while (1) { delay(10); } // Halt
  }

  if (rtc.lostPower()) {
    Serial.println("RTC lost power, let's set the time!");
  }

  // Set the RTC to the date & time this sketch was compiled
  rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

  Serial.println("RTC time has been successfully updated!");
  Serial.print("Current time set to: ");

  DateTime now = rtc.now();
  Serial.print(now.year(), DEC);
  Serial.print('/');
  Serial.print(now.month(), DEC);
  Serial.print('/');
  Serial.print(now.day(), DEC);
  Serial.print(" ");
  Serial.print(now.hour(), DEC);
  Serial.print(':');
  Serial.print(now.minute(), DEC);
  Serial.print(':');
  Serial.println(now.second(), DEC);

  Serial.println("You can now flash the main attendance logger sketch.");
}

void loop() {
  // Nothing to do here
}

Aplicación principal: attendance_logger.ino

Este es el firmware principal del prototipo. Inicializa el bus SPI compartido, gestiona los pines de selección de chip, sondea tarjetas RFID y añade registros a la tarjeta SD.

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

/*
 * Project: RFID Attendance SD Logger
 * Target: Arduino UNO R3 + MFRC522 + MicroSD + DS3231
 * Description: Reads RFID tag UIDs, fetches timestamp from RTC, 
 *              and logs the data to ATTEND.CSV on the SD card.
 */

#include <SPI.h>
#include <MFRC522.h>
#include <SD.h>
#include <Wire.h>
#include <RTClib.h>

// --- Pin Definitions ---
#define SD_CS_PIN    4
#define RFID_CS_PIN  10
#define RFID_RST_PIN 9

// --- Object Instantiation ---
MFRC522 mfrc522(RFID_CS_PIN, RFID_RST_PIN);
RTC_DS3231 rtc;

// --- Global Variables ---
const char* logFileName = "ATTEND.CSV";

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

  Serial.println(F("--- RFID Attendance Logger Initialization ---"));

  // 1. Initialize SPI Bus
  SPI.begin();

  // 2. Initialize DS3231 RTC (I2C)
  if (!rtc.begin()) {
    Serial.println(F("ERROR: Couldn't find RTC."));
    while (1) { delay(10); }
  }
  if (rtc.lostPower()) {
    Serial.println(F("WARNING: RTC lost power. Time may be inaccurate."));
  } else {
    Serial.println(F("RTC initialized successfully."));
  }

  // 3. Initialize MicroSD Card (SPI)
  // Disable RFID SPI temporarily to ensure clean SD init
  pinMode(RFID_CS_PIN, OUTPUT);
  digitalWrite(RFID_CS_PIN, HIGH); 

  Serial.print(F("Initializing SD card..."));
  if (!SD.begin(SD_CS_PIN)) {
    Serial.println(F("ERROR: SD card initialization failed!"));
    Serial.println(F("Check formatting (FAT32), wiring, and CS pin."));
    while (1) { delay(10); } // Halt if SD fails
  }
  Serial.println(F("SD card initialized."));

  // Write CSV Header if file doesn't exist
  if (!SD.exists(logFileName)) {
    File dataFile = SD.open(logFileName, FILE_WRITE);
    if (dataFile) {
      dataFile.println(F("Timestamp,UID"));
      dataFile.close();
      Serial.println(F("Created new ATTEND.CSV with headers."));
    } else {
      Serial.println(F("ERROR: Could not create file on SD card."));
    }
// ...

/*
 * Project: RFID Attendance SD Logger
 * Target: Arduino UNO R3 + MFRC522 + MicroSD + DS3231
 * Description: Reads RFID tag UIDs, fetches timestamp from RTC, 
 *              and logs the data to ATTEND.CSV on the SD card.
 */

#include <SPI.h>
#include <MFRC522.h>
#include <SD.h>
#include <Wire.h>
#include <RTClib.h>

// --- Pin Definitions ---
#define SD_CS_PIN    4
#define RFID_CS_PIN  10
#define RFID_RST_PIN 9

// --- Object Instantiation ---
MFRC522 mfrc522(RFID_CS_PIN, RFID_RST_PIN);
RTC_DS3231 rtc;

// --- Global Variables ---
const char* logFileName = "ATTEND.CSV";

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

  Serial.println(F("--- RFID Attendance Logger Initialization ---"));

  // 1. Initialize SPI Bus
  SPI.begin();

  // 2. Initialize DS3231 RTC (I2C)
  if (!rtc.begin()) {
    Serial.println(F("ERROR: Couldn't find RTC."));
    while (1) { delay(10); }
  }
  if (rtc.lostPower()) {
    Serial.println(F("WARNING: RTC lost power. Time may be inaccurate."));
  } else {
    Serial.println(F("RTC initialized successfully."));
  }

  // 3. Initialize MicroSD Card (SPI)
  // Disable RFID SPI temporarily to ensure clean SD init
  pinMode(RFID_CS_PIN, OUTPUT);
  digitalWrite(RFID_CS_PIN, HIGH); 

  Serial.print(F("Initializing SD card..."));
  if (!SD.begin(SD_CS_PIN)) {
    Serial.println(F("ERROR: SD card initialization failed!"));
    Serial.println(F("Check formatting (FAT32), wiring, and CS pin."));
    while (1) { delay(10); } // Halt if SD fails
  }
  Serial.println(F("SD card initialized."));

  // Write CSV Header if file doesn't exist
  if (!SD.exists(logFileName)) {
    File dataFile = SD.open(logFileName, FILE_WRITE);
    if (dataFile) {
      dataFile.println(F("Timestamp,UID"));
      dataFile.close();
      Serial.println(F("Created new ATTEND.CSV with headers."));
    } else {
      Serial.println(F("ERROR: Could not create file on SD card."));
    }
  }

  // 4. Initialize MFRC522 RFID (SPI)
  mfrc522.PCD_Init();
  // Optional: Increase antenna gain if tags are hard to read
  // mfrc522.PCD_SetAntennaGain(mfrc522.RxGain_max);
  Serial.println(F("MFRC522 initialized successfully."));

  Serial.println(F("--- System Ready. Waiting for RFID tags ---"));
}

void loop() {
  // Look for new RFID cards
  if (!mfrc522.PICC_IsNewCardPresent()) {
    return;
  }

  // Select one of the cards
  if (!mfrc522.PICC_ReadCardSerial()) {
    return;
  }

  // Get Current Time from RTC
  DateTime now = rtc.now();

  // Format Timestamp: YYYY-MM-DD HH:MM:SS
  char timeBuffer[20];
  snprintf(timeBuffer, sizeof(timeBuffer), "%04d-%02d-%02d %02d:%02d:%02d",
           now.year(), now.month(), now.day(),
           now.hour(), now.minute(), now.second());

  // Format UID to Hex String
  String uidString = "";
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    if (mfrc522.uid.uidByte[i] < 0x10) {
      uidString += "0";
    }
    uidString += String(mfrc522.uid.uidByte[i], HEX);
  }
  uidString.toUpperCase();

  // Print to Serial Monitor
  Serial.print(F("Scanned: "));
  Serial.print(timeBuffer);
  Serial.print(F(" | UID: "));
  Serial.println(uidString);

  // Log to SD Card
  File dataFile = SD.open(logFileName, FILE_WRITE);
  if (dataFile) {
    dataFile.print(timeBuffer);
    dataFile.print(F(","));
    dataFile.println(uidString);
    dataFile.close();
    Serial.println(F(" -> Logged to SD card successfully."));
  } else {
    Serial.println(F(" -> ERROR: Failed to open ATTEND.CSV for writing."));
  }

  // Halt PICC to prevent reading the same card repeatedly in a single tap
  mfrc522.PICC_HaltA();
  // Stop encryption on PCD
  mfrc522.PCD_StopCrypto1();

  // Brief delay to prevent rapid-fire multiple reads
  delay(1000); 
}

Comandos de compilación/flasheo/ejecución

Utiliza arduino-cli para compilar y subir tu código.

Tabla de referencia de comandos

Tarea Comando
Actualizar índice de core arduino-cli core update-index
Instalar core AVR arduino-cli core install arduino:avr
Instalar dependencias arduino-cli lib install "MFRC522" "RTClib" "SD"
Compilar sketch arduino-cli compile --fqbn arduino:avr:uno <Sketch_Folder>
Subir al UNO arduino-cli upload --fqbn arduino:avr:uno --port <PORT> <Sketch_Folder>
Monitor serie arduino-cli monitor --port <PORT> --config baudrate=9600

Flujo de trabajo numerado

  1. Encuentra tu puerto: Conecta el Arduino UNO a tu computadora. Ejecuta arduino-cli board list para identificar el <PORT> (ej., COM3 en Windows, /dev/ttyACM0 en Linux).
  2. Instala las bibliotecas: Asegúrate de que las bibliotecas de terceros requeridas estén instaladas:
    arduino-cli lib install "MFRC522" "RTClib" "SD"
  3. Configura la hora del RTC:
    • Guarda el primer bloque de código en una carpeta llamada set_rtc_time.
    • Compilar: arduino-cli compile --fqbn arduino:avr:uno set_rtc_time
    • Subir: arduino-cli upload --fqbn arduino:avr:uno --port <PORT> set_rtc_time
    • Monitor: arduino-cli monitor --port <PORT> --config baudrate=9600 para verificar que se configuró la hora.
  4. Flashea el registrador principal:
    • Guarda el segundo bloque de código en una carpeta llamada attendance_logger.
    • Compilar: arduino-cli compile --fqbn arduino:avr:uno attendance_logger
    • Subir: arduino-cli upload --fqbn arduino:avr:uno --port <PORT> attendance_logger
    • Monitor: arduino-cli monitor --port <PORT> --config baudrate=9600 para ver el sistema en vivo.

Validación paso a paso

Utiliza estos puntos de control mientras monitoreas la salida serie para asegurarte de que tu prototipo sea completamente funcional.

  1. Punto de control: Inicialización del RTC
    • Acción: Reinicia el Arduino y observa el monitor serie.
    • Observación esperada: El monitor imprime «RTC initialized successfully.»
    • Condición de éxito: El sistema no se detiene con «ERROR: Couldn’t find RTC.»
  2. Punto de control: Montaje de la tarjeta SD
    • Acción: Continúa observando el monitor serie durante el arranque.
    • Observación esperada: El monitor imprime «Initializing SD card…» seguido de «SD card initialized.»
    • Condición de éxito: El sistema monta con éxito el sistema de archivos FAT32 y no se detiene.
  3. Punto de control: Inicialización del módulo RFID
    • Acción: Observa los mensajes de arranque finales.
    • Observación esperada: El monitor imprime «MFRC522 initialized successfully.» y «— System Ready. Waiting for RFID tags —«.
    • Condición de éxito: Ambos dispositivos SPI (SD y RFID) se han inicializado sin causar conflictos en el bus.
  4. Punto de control: Escaneo y registro de etiquetas
    • Acción: Toca la antena del MFRC522 con una etiqueta RFID de 13.56 MHz.
    • Observación esperada: El monitor serie muestra «Scanned: [Fecha/Hora] | UID: [Cadena Hexadecimal]» seguido de » -> Logged to SD card successfully.»
    • Condición de éxito: El UID coincide con tu tarjeta, la marca de tiempo es precisa y la escritura en la SD reporta éxito.
  5. Punto de control: Verificación de datos CSV
    • Acción: Apaga el Arduino, retira la tarjeta microSD y léela en tu computadora.
    • Observación esperada: Existe un archivo llamado ATTEND.CSV. Al abrirlo, se revela una fila de encabezado (Timestamp,UID) y una fila por cada toque de tarjeta.
    • Condición de éxito: Los datos están correctamente separados por comas y son legibles para un humano en una aplicación de hoja de cálculo.

Solución de problemas

Síntoma Causa probable Solución
Couldn't find RTC Problema de cableado I2C. Verifica que SDA esté en A4 y SCL en A5. Asegúrate de que el módulo tenga 5V y GND.
SD card initialization failed! Formato incorrecto o pin CS. Asegúrate de que la tarjeta SD esté formateada en FAT32 (no exFAT ni NTFS). Verifica que CS esté conectado al Pin 4.
La SD falla solo cuando el RFID está conectado Conflicto MISO SPI (módulos SD económicos). Algunos módulos SD no liberan la línea MISO cuando su CS está en ALTO. Intenta alimentar el módulo SD desde una fuente de 5V separada o añade un búfer triestado a la línea MISO de la SD.
El RFID no detecta tarjetas Fuente de alimentación o cableado SPI. Asegúrate de que el VCC del MFRC522 sea 3.3V, NO 5V. Verifica MOSI (11), MISO (12), SCK (13), SDA (10) y RST (9).
La marca de tiempo se reinicia a 2000-01-01 Batería del RTC agotada. Reemplaza la batería CR2032 en el módulo DS3231 y vuelve a ejecutar el sketch set_rtc_time.

Mejoras

Una vez que el registrador básico esté funcionando, considera estas mejoras temáticas para un prototipo más robusto:

  • Mecanismos de retroalimentación para el usuario:
    • Añade un zumbador piezoeléctrico para proporcionar un «pitido» audible tras un escaneo y registro exitosos.
    • Integra un LED bicolor (verde para un registro exitoso, rojo para un error de la tarjeta SD) para que el dispositivo pueda usarse sin un monitor serie conectado.
  • Gestión e integridad de datos:
    • Implementa un sistema de rotación diaria de archivos (ej., LOG_1024.CSV para el 24 de octubre) para evitar que un solo archivo CSV se vuelva demasiado grande para analizarlo rápidamente.
    • Añade una base de datos de UID conocidos en la memoria flash del Arduino (usando PROGMEM) para rechazar tarjetas desconocidas y registrar solo usuarios autorizados.
  • Energía e implementación:
    • Añade una rutina de modo de suspensión para apagar los periféricos SPI y poner el ATmega328P a dormir cuando no haya ninguna tarjeta presente, despertando mediante una interrupción de hardware (si usas un módulo RFID diferente que soporte el despertar por IRQ).

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 objetivo principal del prototipo de hardware descrito?




Pregunta 2: ¿Qué componente se utiliza para obtener la hora actual en el prototipo?




Pregunta 3: ¿En qué formato se registran los eventos (UID y marca de tiempo) en la tarjeta microSD?




Pregunta 4: ¿Cuál de los siguientes es un caso de uso mencionado para este proyecto?




Pregunta 5: ¿Qué ventaja ofrece el sistema para talleres pequeños según el texto?




Pregunta 6: ¿Qué tipo de etiquetas RFID lee el prototipo construido?




Pregunta 7: ¿Qué bus de comunicación comparten la tarjeta microSD y el módulo RFID en este prototipo?




Pregunta 8: ¿Qué protocolo de comunicación utiliza el componente del reloj de tiempo real (RTC)?




Pregunta 9: ¿Qué beneficio aporta este sistema en el registro de acceso a equipos de laboratorio?




Pregunta 10: Según el resultado esperado, ¿qué información se debe mostrar en la salida del monitor serie?




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: ventilación de invernadero con Arduino UNO

Caso práctico: ventilación de invernadero con Arduino UNO — hero

Objetivo y caso de uso

Qué construirás: Construirás un controlador de ventilación de invernadero automatizado e independiente que ajusta dinámicamente una compuerta de ventilación física mediante un servomotor en función de la temperatura ambiente. El sistema incorpora una anulación de hardware manual y lógica de histéresis para evitar la oscilación mecánica.

Por qué es importante / Casos de uso

  • Automatización agrícola: Regula invernaderos a pequeña escala de forma autónoma, eliminando la necesidad de monitoreo humano constante para prevenir el estrés de las plantas.
  • Implementación de histéresis: Utiliza umbrales de temperatura superior e inferior distintos (por ejemplo, abrir la compuerta a 26°C, cerrar a 22°C) para estabilizar el sistema y evitar el desgaste del servo por cambios rápidos.
  • Anulaciones de seguridad: Proporciona una derivación manual basada en hardware (mediante un interruptor de fin de carrera) para control inmediato durante emergencias o mantenimiento de rutina.

Resultado esperado

  • Un sistema de bucle cerrado que acciona un servo de 0° (cerrado) a 90° (abierto) con una latencia de respuesta de <50ms cuando se cruzan los umbrales de temperatura.
  • Funcionamiento mecánico suave y sin vibraciones en los límites de temperatura gracias a la histéresis programada.
  • Respuesta mecánica inmediata al interruptor de fin de carrera, reemplazando toda la lógica automatizada del sensor con latencia casi nula.

Audiencia: Makers, estudiantes de tecnología agrícola y desarrolladores de sistemas embebidos; Nivel: Principiante a Intermedio

Arquitectura/flujo: Sensor de temperatura (Entrada analógica) ➔ Microcontrolador (Lógica de histéresis e interrupciones de hardware) ➔ Salida PWM ➔ Actuador de 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 proyecto es un prototipo educativo de bajo voltaje, no un producto certificado. Antes de encender la configuración, verifique el cableado de su Arduino UNO R3, evite cortocircuitar los 5V, GND o pines digitales, desconecte la alimentación antes de cambiar las conexiones y use módulos de interfaz adecuados para relés, motores o cargas externas.

Requisitos previos

Para completar con éxito este tutorial, debe tener:
* Una comprensión básica de cómo usar la línea de comandos/terminal en su sistema operativo.
* La CLI (Interfaz de línea de comandos) de Arduino instalada en su computadora.
* Familiaridad básica con placas de pruebas (protoboards) y conexiones de cables puente (jumpers).
* Un cable USB Tipo-B estándar para conectar el Arduino UNO a su computadora.

Materiales

Para este proyecto, debe usar EXACTAMENTE este modelo de dispositivo y lista de componentes:
* Microcontrolador: Arduino UNO R3 (ATmega328P)
* Actuador: Micro servomotor SG90
* Sensor: Sensor de temperatura analógico LM35 (encapsulado TO-92)
* Dispositivo de entrada: Interruptor de fin de carrera (microinterruptor estándar con palanca de rodillo o botón pulsador básico)
* Accesorios: 1x Placa de pruebas (protoboard) sin soldadura, varios cables puente macho a macho.

Nota: No se requieren resistencias externas para el interruptor de fin de carrera porque utilizaremos las resistencias pull-up internas del ATmega328P mediante software.

Configuración/Conexión

Un cableado adecuado es fundamental para la estabilidad de las lecturas analógicas y los movimientos del servo. El LM35 proporciona una salida de voltaje analógico lineal proporcional a la temperatura (10mV por grado Celsius). El servo SG90 se controla mediante Modulación por Ancho de Pulso (PWM), y el interruptor de fin de carrera utiliza una entrada digital simple.

Tabla de cableado

Componente Pin / Color del cable Pin del Arduino UNO R3 Función / Notas
LM35 Pin 1 (Izquierdo, cara plana hacia arriba) 5V Fuente de alimentación para el sensor de temperatura.
LM35 Pin 2 (Medio) A0 Señal de salida analógica (10mV/°C).
LM35 Pin 3 (Derecho) GND Referencia de tierra.
Servo SG90 Cable rojo 5V Fuente de alimentación para el servomotor.
Servo SG90 Cable marrón / negro GND Referencia de tierra.
Servo SG90 Cable naranja / amarillo D9 Señal PWM para controlar el ángulo del servo.
Interruptor de fin de carrera COM (Común) GND Referencia de tierra para el interruptor.
Interruptor de fin de carrera NO (Normalmente Abierto) D2 Entrada digital. En estado ALTO (HIGH) internamente; cambia a BAJO (LOW) al presionarse.

Instrucciones de conexión

  1. Distribución de energía: Conecte el pin de 5V del Arduino al riel positivo de su placa de pruebas. Conecte el pin GND del Arduino al riel negativo.
  2. Sensor LM35: Inserte el LM35 en la placa de pruebas. Con el lado plano mirándolo a usted, conecte el pin izquierdo al riel de 5V, el pin derecho al riel GND y el pin central directamente al pin analógico A0 del Arduino.
  3. Servo SG90: Conecte los cables de alimentación del servo (rojo a 5V, marrón/negro a GND) a los rieles de la placa de pruebas. Conecte el cable de señal (naranja/amarillo) al pin digital D9 del Arduino.
  4. Interruptor de fin de carrera: Conecte el terminal Común (COM) del interruptor de fin de carrera al riel GND. Conecte el terminal Normalmente Abierto (NO) al pin digital D2 del Arduino.

Código validado

La siguiente sección contiene el sketch de Arduino completo y compilable y un script de shell de Bash utilizado para automatizar el proceso de compilación y carga mediante la CLI de Arduino.

Sketch de Arduino: greenhouse_vent.ino

Cree un directorio llamado greenhouse_vent y guarde este código dentro como greenhouse_vent.ino.

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

/*
 * Greenhouse Vent Servo Controller
 * Device: Arduino UNO R3 (ATmega328P) + SG90 servo + LM35 temperature sensor + limit switch
 * 
 * Description: Reads temperature from LM35. Opens vent (servo 90 deg) if temp >= 28C.
 * Closes vent (servo 0 deg) if temp <= 25C. 
 * A limit switch on D2 acts as a manual override to force the vent open.
 */

#include <Servo.h>

// Pin Definitions
const int lm35Pin = A0;
const int limitSwitchPin = 2;
const int servoPin = 9;

// Servo Object
Servo ventServo;

// Configuration Constants
const float TEMP_OPEN_THRESHOLD = 28.0;
const float TEMP_CLOSE_THRESHOLD = 25.0;
const int ANGLE_CLOSED = 0;
const int ANGLE_OPEN = 90;

// Timing Variables for non-blocking execution
unsigned long lastUpdateMillis = 0;
const unsigned long UPDATE_INTERVAL_MS = 1000;

// State Tracking
bool ventIsOpen = false;

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

  // Configure Pins
  // Internal pull-up ensures the pin reads HIGH when the switch is unpressed.
  // When pressed, the switch connects the pin to GND, reading LOW.
  pinMode(limitSwitchPin, INPUT_PULLUP);

  // Attach and initialize servo to closed position
  ventServo.attach(servoPin);
  ventServo.write(ANGLE_CLOSED);

  Serial.println("========================================");
  Serial.println("Greenhouse Vent Controller Initialized");
  Serial.println("========================================");
}
// ...

/*
 * Greenhouse Vent Servo Controller
 * Device: Arduino UNO R3 (ATmega328P) + SG90 servo + LM35 temperature sensor + limit switch
 * 
 * Description: Reads temperature from LM35. Opens vent (servo 90 deg) if temp >= 28C.
 * Closes vent (servo 0 deg) if temp <= 25C. 
 * A limit switch on D2 acts as a manual override to force the vent open.
 */

#include <Servo.h>

// Pin Definitions
const int lm35Pin = A0;
const int limitSwitchPin = 2;
const int servoPin = 9;

// Servo Object
Servo ventServo;

// Configuration Constants
const float TEMP_OPEN_THRESHOLD = 28.0;
const float TEMP_CLOSE_THRESHOLD = 25.0;
const int ANGLE_CLOSED = 0;
const int ANGLE_OPEN = 90;

// Timing Variables for non-blocking execution
unsigned long lastUpdateMillis = 0;
const unsigned long UPDATE_INTERVAL_MS = 1000;

// State Tracking
bool ventIsOpen = false;

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

  // Configure Pins
  // Internal pull-up ensures the pin reads HIGH when the switch is unpressed.
  // When pressed, the switch connects the pin to GND, reading LOW.
  pinMode(limitSwitchPin, INPUT_PULLUP);

  // Attach and initialize servo to closed position
  ventServo.attach(servoPin);
  ventServo.write(ANGLE_CLOSED);

  Serial.println("========================================");
  Serial.println("Greenhouse Vent Controller Initialized");
  Serial.println("========================================");
}

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

  // Execute control logic at defined intervals
  if (currentMillis - lastUpdateMillis >= UPDATE_INTERVAL_MS) {
    lastUpdateMillis = currentMillis;

    // 1. Read Manual Override Limit Switch
    // LOW means the switch is pressed (override active)
    bool overrideActive = (digitalRead(limitSwitchPin) == LOW);

    // 2. Read and Calculate Temperature from LM35
    int rawADC = analogRead(lm35Pin);

    // The Arduino UNO has a 10-bit ADC (0-1023) and operates at 5.0V.
    // Voltage = (ADC Value / 1024.0) * 5.0
    float voltage = rawADC * (5.0 / 1024.0);

    // LM35 outputs 10mV per degree Celsius (0.01V/C)
    // Temperature (C) = Voltage / 0.01 = Voltage * 100.0
    float temperatureC = voltage * 100.0;

    // 3. Determine Target Vent State
    if (overrideActive) {
      // Manual override forces the vent open
      ventIsOpen = true;
    } else {
      // Temperature-based hysteresis control
      if (temperatureC >= TEMP_OPEN_THRESHOLD) {
        ventIsOpen = true;
      } else if (temperatureC <= TEMP_CLOSE_THRESHOLD) {
        ventIsOpen = false;
      }
      // If temperature is between 25.0 and 28.0, ventIsOpen remains unchanged.
    }

    // 4. Actuate Servo
    if (ventIsOpen) {
      ventServo.write(ANGLE_OPEN);
    } else {
      ventServo.write(ANGLE_CLOSED);
    }

    // 5. Log System State
    Serial.print("Temp: ");
    Serial.print(temperatureC, 1);
    Serial.print(" C | Override: ");
    Serial.print(overrideActive ? "ACTIVE " : "STANDBY");
    Serial.print(" | Vent State: ");
    Serial.println(ventIsOpen ? "OPEN  (90 deg)" : "CLOSED (0 deg)");
  }
}

Script de automatización: build_and_upload.sh

Guarde este archivo en el directorio principal de greenhouse_vent (o ajuste las rutas en consecuencia). Este script asegura que el núcleo Arduino AVR esté instalado, compila el código y lo carga en la placa.

#!/bin/bash

# Define the FQBN for Arduino UNO R3
FQBN="arduino:avr:uno"

# Define the serial port (Change this to match your system, e.g., /dev/ttyACM0 or COM3)
PORT="/dev/ttyACM0"

# Define the sketch directory
SKETCH_DIR="greenhouse_vent"

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

echo "Installing Arduino AVR core..."
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

    if [ $? -eq 0 ]; then
        echo "Upload complete! Open serial monitor at 9600 baud."
    else
        echo "Upload failed. Please check the PORT and connection."
    fi
else
    echo "Compilation failed. Please check the source code."
fi

Comandos de compilación/flasheo/ejecución

Para desplegar el código en su Arduino UNO R3, utilizará la CLI de Arduino. A continuación se muestran los comandos exactos utilizados por el script de automatización, que también puede ejecutar manualmente.

Tabla de referencia de comandos

Acción Comando
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 greenhouse_vent
Cargar en la placa arduino-cli upload --fqbn arduino:avr:uno --port <PORT> greenhouse_vent
Monitorizar salida arduino-cli monitor --port <PORT> --config baudrate=9600

Flujo de trabajo numerado

  1. Conecte su Arduino UNO R3 a su computadora a través del cable USB.
  2. Identifique su puerto serie. En Linux, este es típicamente /dev/ttyACM0 o /dev/ttyUSB0. En Windows, será un puerto COM como COM3. Puede encontrarlo ejecutando arduino-cli board list.
  3. Abra su terminal y navegue hasta el directorio que contiene su carpeta greenhouse_vent.
  4. Ejecute el comando de compilación: arduino-cli compile --fqbn arduino:avr:uno greenhouse_vent.
  5. Ejecute el comando de carga, reemplazando <PORT> con su puerto real: arduino-cli upload --fqbn arduino:avr:uno --port /dev/ttyACM0 greenhouse_vent.
  6. Inicie el monitor serie para ver los registros: arduino-cli monitor --port /dev/ttyACM0 --config baudrate=9600.

Validación paso a paso

Una vez que el código esté cargado y el monitor serie esté en ejecución, realice los siguientes puntos de control de validación para asegurarse de que la lógica y el hardware funcionen correctamente.

  • Punto de control 1: Inicialización y estado de reposo

    • Acción: Observe el monitor serie inmediatamente después de la carga, con la temperatura ambiente por debajo de 25°C.
    • Observación esperada: El monitor serie imprime el banner de inicialización. El servo se mueve a la posición de 0 grados. Los registros indican una temperatura por debajo de 25.0°C, Override: STANDBY y Vent State: CLOSED.
    • Condición de aprobación: El servo está físicamente en la posición cero y los registros reflejan el estado cerrado con precisión.
  • Punto de control 2: Calentamiento (Cruzando el umbral superior)

    • Acción: Pellizque suavemente el sensor LM35 entre sus dedos para elevar su temperatura. Observe el monitor serie.
    • Observación esperada: La temperatura registrada aumentará de manera constante. Una vez que alcance 28.0°C o más, el estado de ventilación (Vent State) cambiará a OPEN.
    • Condición de aprobación: El servo SG90 gira físicamente 90 grados inmediatamente cuando el registro muestra una temperatura $\ge$ 28.0°C.
  • Punto de control 3: Verificación de histéresis (Enfriamiento)

    • Acción: Suelte el LM35 y permita que se enfríe. Observe los registros a medida que la temperatura desciende entre 27.9°C y 25.1°C.
    • Observación esperada: La temperatura desciende, pero el estado de ventilación (Vent State) permanece en OPEN. El servo no se mueve.
    • Condición de aprobación: El sistema mantiene su estado actual mientras se encuentra en la banda muerta de histéresis, evitando una oscilación mecánica rápida.
  • Punto de control 4: Cruzando el umbral inferior

    • Acción: Continúe dejando que el sensor se enfríe (o sople suavemente sobre él) hasta que la temperatura descienda a 25.0°C o menos.
    • Observación esperada: El registro actualiza el estado de ventilación (Vent State) a CLOSED.
    • Condición de aprobación: El servo gira físicamente de vuelta a la posición de 0 grados.
  • Punto de control 5: Activación de anulación manual

    • Acción: Mientras la temperatura esté por debajo de 25°C (ventilación normalmente cerrada), mantenga presionado el interruptor de fin de carrera.
    • Observación esperada: El siguiente registro serie (dentro de 1 segundo) muestra Override: ACTIVE y Vent State: OPEN.
    • Condición de aprobación: El servo se mueve inmediatamente a 90 grados a pesar de la temperatura fría. Al soltar el interruptor, el servo debería volver a 0 grados en el siguiente ciclo.

Solución de problemas

Si su prototipo no se comporta como se esperaba, consulte la tabla a continuación para ver problemas comunes y sus soluciones.

Síntoma Causa probable Solución
El LM35 lee ~0°C o ~500°C constantemente Sensor cableado al revés o tierra flotante. Desconecte la alimentación inmediatamente. Compruebe la cara plana del LM35. Izquierda es 5V, Derecha es GND, Centro es A0. Vuelva a asentar los cables puente.
Las lecturas de temperatura fluctúan bruscamente (±5°C)

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 controlador construirás según el texto?




Pregunta 2: ¿Qué componente físico se utiliza para ajustar la compuerta de ventilación?




Pregunta 3: ¿Cuál es el propósito principal de implementar la lógica de histéresis en este sistema?




Pregunta 4: ¿Cómo se activa la anulación de seguridad manual (hardware override)?




Pregunta 5: Según el ejemplo del texto, ¿cómo funcionan los umbrales de temperatura con la histéresis?




Pregunta 6: ¿Cuál es el rango de movimiento esperado para el servomotor en este sistema cerrado?




Pregunta 7: ¿Cuál es la latencia de respuesta esperada del sistema de bucle cerrado?




Pregunta 8: ¿Qué problema elimina la automatización agrícola en este caso de uso?




Pregunta 9: ¿Para qué situaciones se proporciona la derivación manual basada en hardware?




Pregunta 10: ¿Qué función cumple el interruptor de fin de carrera en el sistema?




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: Nodo de relé RS485 con Arduino UNO

Caso práctico: Nodo de relé RS485 con Arduino UNO — hero

Objetivo y caso de uso

Qué construirás: Un nodo de relé remoto robusto y direccionable utilizando un Arduino UNO que se comunica a través de un bus RS485 de larga distancia para conmutar un relé de 5V basándose en comandos serie dirigidos.

Por qué es importante / Casos de uso

  • Automatización agrícola: Control de válvulas de riego o ventiladores de invernaderos a cientos de metros de distancia donde las señales estándar UART de 3.3V, I2C o SPI se degradarían.
  • Sistemas de alerta industrial: Activación de balizas visuales en plantas de fábrica ruidosas. La señalización diferencial RS485 proporciona una alta inmunidad a la interferencia electromagnética (EMI) de la maquinaria pesada.
  • Domótica cableada: Creación de una red cableada y determinista de interruptores distribuidos que garantiza tiempos de respuesta inferiores a 10 ms sin depender de la cobertura Wi-Fi.
  • Redes multipunto escalables: Conexión de hasta 32 nodos direccionables en un solo par trenzado de cables, asegurando que cada nodo solo reaccione a los comandos que coincidan con su ID específico.

Resultado esperado

  • El Arduino interpreta correctamente los comandos serie direccionados y conmuta el relé de 5V con una latencia cercana a cero.
  • Se mantiene una comunicación sin errores en tendidos de cable largos (hasta 1200 metros a 9600 baudios).
  • El nodo filtra correctamente el tráfico del bus, ignorando los comandos destinados a otros dispositivos en la red compartida.

Audiencia: Desarrolladores de sistemas embebidos e ingenieros de automatización; Nivel: Intermedio

Arquitectura/flujo: Controlador Maestro → Bus RS485 (Par trenzado) → Transceptor MAX485 → Arduino UNO (UART) → GPIO → Módulo de Relé de 5V

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 de bajo voltaje, no un producto certificado. Antes de encender la configuración, verifique el cableado de su Arduino UNO R3, evite cortocircuitar los 5 V, GND o los pines digitales, desconecte la alimentación antes de cambiar las conexiones y utilice módulos de interfaz adecuados para relés, motores o cargas externas.

Diagrama de bloques conceptual

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

Arquitectura funcional

Botón local

ESP32 BLE

Paquete advertising

LED de estado

Móvil escáner

Flujo conceptual: configuración local, emisión BLE y lectura desde el móvil.

Requisitos previos

  • Familiaridad con operaciones básicas de terminal e interfaces de línea de comandos.
  • Comprensión de la comunicación UART (Serie) estándar (Tasa de baudios, TX/RX).
  • Arduino CLI instalado y agregado al PATH de su sistema.
  • Python 3.11 instalado (para ejecutar el script maestro de validación).
  • La biblioteca pyserial instalada en su entorno de Python (pip install pyserial).

Materiales

  • Microcontrolador: Arduino UNO R3 (ATmega328P).
  • Transceptor: Módulo MAX485 RS485 (placa de conexión estándar de 8 pines con pines DI, DE, RE, RO).
  • Actuador: Módulo de relé de 5 V (optoaislado, activo en alto o activo en bajo).
  • Interfaz Maestra: Adaptador USB a RS485 (para conectar su PC al bus RS485 para pruebas).
  • Componentes pasivos: 1x resistencia de 120 ohmios (para terminación del bus, recomendada para tendidos de cable largos).
  • Cableado: Cables puente (jumper), protoboard y un cable de par trenzado para las líneas A/B de RS485.

Configuración/Conexión

RS485 es un estándar half-duplex, lo que significa que los datos viajan en ambas direcciones, pero solo en una dirección a la vez. El chip MAX485 utiliza los pines Driver Enable (DE) y Receiver Enable (RE) para alternar entre la transmisión y la recepción. Uniremos estos dos pines y los controlaremos con un solo pin digital en el Arduino.

Utilizaremos SoftwareSerial en los pines 10 y 11 para comunicarnos con el MAX485. Esto preserva el puerto serie de hardware del Arduino (pines 0 y 1) para la depuración por USB y la carga de código sin necesidad de desconectar el cableado.

Cableado del Arduino al Módulo MAX485

Pin del MAX485 Pin del Arduino UNO Función / Descripción
VCC 5V Fuente de alimentación para el transceptor.
GND GND Referencia de tierra común.
RO (Receiver Out) Pin Digital 10 Se conecta al RX de Software del Arduino.
RE (Receiver Enable) Pin Digital 2 Activo en BAJO. Unido a DE.
DE (Driver Enable) Pin Digital 2 Activo en ALTO. Unido a RE.
DI (Driver In) Pin Digital 11 Se conecta al TX de Software del Arduino.
A USB-RS485 ‘A’ Línea de bus RS485 no inversora.
B USB-RS485 ‘B’ Línea de bus RS485 inversora.

Nota: Asegúrese de que la tierra del adaptador USB a RS485 esté conectada a la tierra del Arduino para mantener un voltaje de referencia común, especialmente para pruebas de banco a corta distancia.

Cableado del Arduino al Módulo de Relé de 5V

Pin del Módulo de Relé Pin del Arduino UNO Función / Descripción
VCC / DC+ 5V Fuente de alimentación para la bobina del relé y el optoacoplador.
GND / DC- GND Referencia de tierra común.
IN / Signal Pin Digital 7 Señal de control para activar el relé.

Código validado

Las siguientes secciones contienen el código fuente completo tanto para el nodo Arduino como para el script maestro en Python utilizado para validar el bus.

Firmware del Nodo Arduino

Cree un directorio llamado rs485_relay_node y guarde el siguiente código como rs485_relay_node.ino en su interior.

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

/*
 * rs485_relay_node.ino
 * 
 * Implements a half-duplex RS485 slave node.
 * Listens for "<NODE_ID>:<COMMAND>\n".
 * Valid commands: "ON", "OFF".
 */

#include <SoftwareSerial.h>

// Pin Definitions
#define RE_DE_PIN 2     // HIGH = Transmit, LOW = Receive
#define RELAY_PIN 7     // Relay control pin
#define RX_PIN 10       // SoftwareSerial RX
#define TX_PIN 11       // SoftwareSerial TX

// Node Configuration
const String NODE_ID = "N1";
const long BAUD_RATE = 9600;

// Initialize SoftwareSerial for RS485 communication
SoftwareSerial rs485(RX_PIN, TX_PIN);

void setup() {
  // Configure RS485 control pins
  pinMode(RE_DE_PIN, OUTPUT);
  digitalWrite(RE_DE_PIN, LOW); // Default to listen mode

  // Configure Relay pin
  pinMode(RELAY_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, LOW); // Default relay state (adjust if active-low)

  // Initialize Serial ports
  Serial.begin(BAUD_RATE); // Hardware serial for debugging
  rs485.begin(BAUD_RATE);  // Software serial for RS485 bus

  Serial.println("System Boot: RS485 Relay Node");
  Serial.println("Node ID: " + NODE_ID);
  Serial.println("Listening for commands...");
}

void loop() {
  // Check if data is available on the RS485 bus
  if (rs485.available()) {
    // Read the incoming frame until a newline character
    String incomingMsg = rs485.readStringUntil('\n');
    incomingMsg.trim(); // Remove carriage returns or trailing spaces

    if (incomingMsg.length() > 0) {
      Serial.println("Bus traffic detected: " + incomingMsg);
      processCommand(incomingMsg);
    }
// ...

/*
 * rs485_relay_node.ino
 * 
 * Implements a half-duplex RS485 slave node.
 * Listens for "<NODE_ID>:<COMMAND>\n".
 * Valid commands: "ON", "OFF".
 */

#include <SoftwareSerial.h>

// Pin Definitions
#define RE_DE_PIN 2     // HIGH = Transmit, LOW = Receive
#define RELAY_PIN 7     // Relay control pin
#define RX_PIN 10       // SoftwareSerial RX
#define TX_PIN 11       // SoftwareSerial TX

// Node Configuration
const String NODE_ID = "N1";
const long BAUD_RATE = 9600;

// Initialize SoftwareSerial for RS485 communication
SoftwareSerial rs485(RX_PIN, TX_PIN);

void setup() {
  // Configure RS485 control pins
  pinMode(RE_DE_PIN, OUTPUT);
  digitalWrite(RE_DE_PIN, LOW); // Default to listen mode

  // Configure Relay pin
  pinMode(RELAY_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, LOW); // Default relay state (adjust if active-low)

  // Initialize Serial ports
  Serial.begin(BAUD_RATE); // Hardware serial for debugging
  rs485.begin(BAUD_RATE);  // Software serial for RS485 bus

  Serial.println("System Boot: RS485 Relay Node");
  Serial.println("Node ID: " + NODE_ID);
  Serial.println("Listening for commands...");
}

void loop() {
  // Check if data is available on the RS485 bus
  if (rs485.available()) {
    // Read the incoming frame until a newline character
    String incomingMsg = rs485.readStringUntil('\n');
    incomingMsg.trim(); // Remove carriage returns or trailing spaces

    if (incomingMsg.length() > 0) {
      Serial.println("Bus traffic detected: " + incomingMsg);
      processCommand(incomingMsg);
    }
  }
}

void processCommand(String msg) {
  // Check if the message is addressed to this specific node
  if (msg.startsWith(NODE_ID + ":")) {
    // Extract the command portion after the colon
    int separatorIndex = msg.indexOf(':');
    String action = msg.substring(separatorIndex + 1);

    if (action == "ON") {
      digitalWrite(RELAY_PIN, HIGH);
      Serial.println("Action: Relay turned ON");
      sendAcknowledgment("ACK_ON");
    } 
    else if (action == "OFF") {
      digitalWrite(RELAY_PIN, LOW);
      Serial.println("Action: Relay turned OFF");
      sendAcknowledgment("ACK_OFF");
    }
    else {
      Serial.println("Warning: Unknown command received.");
    }
  } else {
    // Message is for another node or malformed; ignore quietly
    Serial.println("Ignored: Addressed to another node.");
  }
}

void sendAcknowledgment(String ackMsg) {
  // 1. Switch MAX485 to Transmit mode
  digitalWrite(RE_DE_PIN, HIGH);

  // 2. Allow a brief moment for the transceiver to stabilize
  delay(5); 

  // 3. Transmit the acknowledgment frame
  String fullAck = NODE_ID + ":" + ackMsg;
  rs485.println(fullAck);

  // 4. Wait for the serial transmission buffer to empty completely
  // This is critical. If we pull RE_DE low too early, the transmission is cut off.
  rs485.flush(); 

  // 5. Allow the final bit to hit the wire before switching states
  delay(5);

  // 6. Switch MAX485 back to Receive mode
  digitalWrite(RE_DE_PIN, LOW);

  Serial.println("Transmitted: " + fullAck);
}

Script de Prueba Maestro en Python

Guarde el siguiente código como master_controller.py en su PC. Este script actúa como el controlador central, generando tráfico RS485 para probar el nodo Arduino.

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

#!/usr/bin/env python3
"""
master_controller.py
Validates the Arduino RS485 Relay Node by sending specific string commands
over a USB-to-RS485 adapter and waiting for acknowledgments.
"""

import serial
import time
import sys

# Windows: 'COM3', Linux: '/dev/ttyUSB0', macOS: '/dev/cu.usbserial-...'
PORT = '/dev/ttyUSB0' 
BAUD = 9600
TIMEOUT = 2.0

def send_and_wait(ser, node_id, command):
    """Sends a formatted command and waits for an acknowledgment."""
    frame = f"{node_id}:{command}\n"
    print(f"\n[Master] Sending : {frame.strip()}")

    # Send the byte-encoded string over the serial port
    ser.write(frame.encode('ascii'))

    # Wait for the node to process and respond
    time.sleep(0.1)

    # Read the response if available
    response_received = False
    start_time = time.time()

    while (time.time() - start_time) < TIMEOUT:
        if ser.in_waiting > 0:
            response = ser.readline().decode('ascii', errors='ignore').strip()
            print(f"[Master] Received: {response}")
            response_received = True
            break
        time.sleep(0.05)

    if not response_received:
        print("[Master] Error: No response received (Timeout).")
# ...

#!/usr/bin/env python3
"""
master_controller.py
Validates the Arduino RS485 Relay Node by sending specific string commands
over a USB-to-RS485 adapter and waiting for acknowledgments.
"""

import serial
import time
import sys

# Windows: 'COM3', Linux: '/dev/ttyUSB0', macOS: '/dev/cu.usbserial-...'
PORT = '/dev/ttyUSB0' 
BAUD = 9600
TIMEOUT = 2.0

def send_and_wait(ser, node_id, command):
    """Sends a formatted command and waits for an acknowledgment."""
    frame = f"{node_id}:{command}\n"
    print(f"\n[Master] Sending : {frame.strip()}")

    # Send the byte-encoded string over the serial port
    ser.write(frame.encode('ascii'))

    # Wait for the node to process and respond
    time.sleep(0.1)

    # Read the response if available
    response_received = False
    start_time = time.time()

    while (time.time() - start_time) < TIMEOUT:
        if ser.in_waiting > 0:
            response = ser.readline().decode('ascii', errors='ignore').strip()
            print(f"[Master] Received: {response}")
            response_received = True
            break
        time.sleep(0.05)

    if not response_received:
        print("[Master] Error: No response received (Timeout).")

def main():
    try:
        print(f"Opening port {PORT} at {BAUD} baud...")
        with serial.Serial(PORT, BAUD, timeout=TIMEOUT) as ser:
            time.sleep(2) # Allow port initialization

            print("--- Starting Validation Sequence ---")

            # Test 1: Turn Relay ON
            send_and_wait(ser, "N1", "ON")
            time.sleep(2)

            # Test 2: Turn Relay OFF
            send_and_wait(ser, "N1", "OFF")
            time.sleep(2)

            # Test 3: Test Address Filtering (Should be ignored by N1)
            send_and_wait(ser, "N2", "ON")

            print("\n--- Validation Sequence Complete ---")

    except serial.SerialException as e:
        print(f"Serial Port Error: {e}")
        sys.exit(1)
    except KeyboardInterrupt:
        print("\nProcess aborted by user.")
        sys.exit(0)

if __name__ == '__main__':
    main()

Comandos de Compilación/Flasheo/Ejecución

Utilice Arduino CLI para compilar y flashear el firmware en su Arduino UNO R3. Asegúrese de que su terminal esté abierta en el directorio que contiene rs485_relay_node.

Tarea Comando
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 rs485_relay_node
Subir a la placa arduino-cli upload --fqbn arduino:avr:uno --port <PORT> rs485_relay_node

Pasos del flujo de trabajo:
1. Conecte el Arduino UNO a su PC mediante USB.
2. Identifique el puerto (por ejemplo, COM3 en Windows o /dev/ttyACM0 en Linux) utilizando arduino-cli board list.
3. Ejecute el comando de compilación para asegurarse de que no haya errores de sintaxis.
4. Ejecute el comando de subida, reemplazando <PORT> con su identificador de puerto específico.
5. Abra un monitor serie (`arduino-

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é componente principal se utiliza como microcontrolador en este proyecto?




Pregunta 2: ¿Qué protocolo de comunicación de larga distancia se utiliza en este proyecto?




Pregunta 3: ¿Qué tipo de actuador se conmuta basándose en los comandos serie?




Pregunta 4: ¿Por qué es útil el RS485 en la automatización agrícola según el texto?




Pregunta 5: ¿Qué característica del RS485 proporciona alta inmunidad a la interferencia electromagnética (EMI) en entornos industriales?




Pregunta 6: En el contexto de la domótica cableada, ¿qué ventaja ofrece este sistema frente al Wi-Fi?




Pregunta 7: ¿Cuántos nodos direccionables se pueden conectar como máximo en un solo par trenzado de cables según el texto?




Pregunta 8: ¿Qué tipo de cableado físico se menciona para conectar los nodos en la red multipunto?




Pregunta 9: ¿Cómo sabe un nodo en la red multipunto que debe reaccionar a un comando?




Pregunta 10: ¿Qué protocolos de comunicación estándar se menciona que se degradarían a cientos de metros de distancia?




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 de nevera abierta con Arduino UNO

Caso práctico: alarma de nevera abierta con Arduino UNO — hero

Objetivo y caso de uso

Qué construirás: Un prototipo de monitor ambiental y sistema de alarma que rastrea el estado de la puerta del refrigerador y la temperatura interna para evitar el deterioro de los alimentos y el desperdicio de energía.

Por qué es importante / Casos de uso

  • Almacenamiento comercial y de laboratorio: Evita que las cámaras frigoríficas queden entreabiertas y garantiza que las muestras químicas sensibles a la temperatura permanezcan dentro de umbrales térmicos estrictamente definidos.
  • Conservación de energía doméstica: Elimina el desperdicio de energía y evita el quemado del compresor causado por puertas abiertas de forma prolongada en entornos residenciales.
  • Educación sobre máquinas de estado de hardware: Demuestra código de microcontrolador práctico sin bloqueo utilizando millis() para manejar múltiples sensores independientes simultáneamente sin bloqueo de subprocesos.

Resultado esperado

  • El sistema consulta continuamente el sensor de temperatura LM35 cada 2000ms (0.5 Hz) y transmite los datos al Monitor Serie para su registro.
  • Un interruptor de láminas magnético detecta con precisión los estados de puerta abierta/cerrada con una latencia cercana a cero, activando transiciones inmediatas de la máquina de estados y alarmas temporales.

Audiencia: Desarrolladores de sistemas embebidos, Educadores STEM; Nivel: Intermedio

Arquitectura/flujo: LM35 (Analógico) & Interruptor de láminas (GPIO Digital) → Microcontrolador (Máquina de estados sin bloqueo) → Registro en Monitor Serie & Disparadores de alarma

Nota educativa de validación

Antes de publicar este caso, el contenido pasó la puerta automática de validación de Prometeo con estado PASS. El validador comprobó los bloques de código, la estructura del artículo, los comandos copiables y la coherencia con el catálogo de dispositivos soportados.

Evidencia de validación publicada

  • Resultado automático: PASS.
  • Estructura parseada: 3 apartados, 1 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 de bajo voltaje, no un producto certificado. Antes de encender la configuración, verifique el cableado de su Arduino UNO R3, evite cortocircuitar los 5 V, GND o los pines digitales, desconecte la alimentación antes de cambiar las conexiones y use módulos de interfaz adecuados para relés, motores o cargas externas.

Diagrama de bloques conceptual

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

Arquitectura funcional

LM35 (Analógico) & Interruptor de láminas…

Microcontrolador (Máquina de estados sin…

Registro en Monitor Serie & Disparadores…

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.

Método de validación y precisión

Para validar la precisión de las lecturas del sensor LM35, coloque un termómetro digital comercial calibrado exactamente en la misma ubicación que el LM35 dentro del refrigerador.
* Evidencia esperada: La salida serie del Arduino debería informar temperaturas dentro de ±0.5°C del termómetro calibrado a las temperaturas estándar del refrigerador (2.0°C a 8.0°C) después de permitir 10 minutos para que la carcasa del sensor alcance el equilibrio térmico.

Requisitos previos

Antes de comenzar este tutorial, asegúrese de tener listas las siguientes herramientas y conocimientos:
* Una computadora host con Linux, macOS o Windows.
* Arduino CLI instalado y agregado al PATH de su sistema.
* Un cable USB Tipo-A a Tipo-B para conectar el Arduino UNO a su computadora.
* Comprensión básica de cómo usar una protoboard y cables puente.

Materiales

Necesitará los componentes exactos enumerados a continuación para construir este prototipo.

Componente Modelo exacto / Especificación Cantidad Papel en el prototipo
Microcontrolador Arduino UNO R3 (ATmega328P) 1 La unidad central de procesamiento que evalúa la lógica de los sensores.
Sensor de temperatura LM35 (Paquete LM35DZ TO-92) 1 Sensor analógico de precisión que proporciona una salida lineal de 10mV/°C.
Sensor magnético Interruptor de láminas estándar (Normalmente abierto) 1 Detecta la presencia de un imán para determinar el estado de la puerta.
Imán Pequeño imán de neodimio o cerámica 1 Montado en la puerta para accionar el interruptor de láminas.
Actuador de audio Zumbador piezoeléctrico (Pasivo o Activo) 1 Emite tonos de alarma audibles.
Cableado Cables puente macho a macho ~10 Conecta los componentes en la protoboard al Arduino.
Protoboard Protoboard estándar de tamaño medio 1 Proporciona una base de creación de prototipos sin soldadura.

Nota: No requerimos una resistencia pull-up externa para el interruptor de láminas porque utilizaremos la resistencia pull-up interna del ATmega328P mediante software.

Configuración/Conexión

Siga estas instrucciones cuidadosamente. Asegúrese de que su Arduino esté desconectado de la alimentación USB mientras realiza el cableado.

1. Cableado del sensor de temperatura LM35

Sostenga el LM35 de modo que la cara plana con el texto impreso quede frente a usted y los tres pines apunten hacia abajo.
* Pin izquierdo (VCC): Conéctelo al pin de 5V en el Arduino.
* Pin central (OUT): Conéctelo al pin A0 (Analógico 0) en el Arduino.
* Pin derecho (GND): Conéctelo a un pin GND en el Arduino.

2. Cableado del interruptor de láminas

Un interruptor de láminas tiene dos terminales y no está polarizado (la orientación no importa).
* Terminal 1: Conéctelo al pin digital 2 en el Arduino.
* Terminal 2: Conéctelo a un pin GND en el Arduino.
* Nota lógica: Configuraremos el pin 2 como INPUT_PULLUP. Cuando el imán está cerca (puerta cerrada), el interruptor se cierra, conectando el pin 2 a GND (leyendo LOW). Cuando la puerta se abre, el interruptor se abre y la resistencia interna eleva el pin 2 a 5V (leyendo HIGH).

3. Cableado del zumbador piezoeléctrico

Si su zumbador piezoeléctrico tiene una pata más larga (positiva) y una pata más corta (negativa), siga esta polarización.
* Terminal positivo (Pata más larga / Cable rojo): Conéctelo al pin digital 8 en el Arduino.
* Terminal negativo (Pata más corta / Cable negro): Conéctelo a un pin GND en el Arduino.

Sketch de Arduino

Cree un nuevo directorio llamado FridgeAlarm y cree el siguiente archivo dentro de él llamado FridgeAlarm.ino.

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

/*
 * Fridge Door and Temperature Alarm
 * Architecture: Arduino UNO R3 (ATmega328P)
 * Sensors: Reed Switch (Pin 2), LM35 (Pin A0)
 * Actuator: Piezo Buzzer (Pin 8)
 */

// Hardware Pin Definitions
const int REED_PIN = 2;
const int BUZZER_PIN = 8;
const int LM35_PIN = A0;

// Configuration Thresholds
const float TEMP_THRESHOLD_C = 8.0;          // Alarm triggers if temperature exceeds 8.0 Celsius
const unsigned long DOOR_TIMEOUT_MS = 20000; // Alarm triggers if door is open for 20,000 ms (20 seconds)
const unsigned long TEMP_READ_INTERVAL = 2000; // Read temperature every 2,000 ms (2 seconds)

// State Tracking Variables
unsigned long doorOpenedAt = 0;
unsigned long lastTempRead = 0;

bool isDoorOpen = false;
bool tempAlarmActive = false;
bool doorAlarmActive = false;
float currentTemp = 0.0;

void setup() {
    // Initialize serial communication for data logging
    Serial.begin(115200);

    // Configure pins
    // INPUT_PULLUP applies an internal 20k-50k ohm resistor to 5V.
    pinMode(REED_PIN, INPUT_PULLUP);
    pinMode(BUZZER_PIN, OUTPUT);

    Serial.println("--- Fridge Monitor Initialized ---");
    Serial.print("Temp Threshold: ");
    Serial.print(TEMP_THRESHOLD_C);
    Serial.println(" C");
    Serial.print("Door Timeout: ");
    Serial.print(DOOR_TIMEOUT_MS / 1000);
    Serial.println(" seconds");
}

void loop() {
    // Capture current time once per loop iteration
    unsigned long currentMillis = millis();

    // ---------------------------------------------------------
    // 1. Evaluate Door State
    // ---------------------------------------------------------
    // Switch is closed (LOW) when magnet is present (door closed).
    // Switch is open (HIGH) when magnet is removed (door open).
    bool currentDoorState = (digitalRead(REED_PIN) == HIGH);

    // Detect state change: Door just opened
    if (currentDoorState && !isDoorOpen) {
        isDoorOpen = true;
        doorOpenedAt = currentMillis;
        Serial.println("[EVENT] Door Opened.");
    } 
    // Detect state change: Door just closed
    else if (!currentDoorState && isDoorOpen) {
        isDoorOpen = false;
        doorAlarmActive = false;
        Serial.println("[EVENT] Door Closed.");
        noTone(BUZZER_PIN); // Silence any active door alarms
    }
// ...

/*
 * Fridge Door and Temperature Alarm
 * Architecture: Arduino UNO R3 (ATmega328P)
 * Sensors: Reed Switch (Pin 2), LM35 (Pin A0)
 * Actuator: Piezo Buzzer (Pin 8)
 */

// Hardware Pin Definitions
const int REED_PIN = 2;
const int BUZZER_PIN = 8;
const int LM35_PIN = A0;

// Configuration Thresholds
const float TEMP_THRESHOLD_C = 8.0;          // Alarm triggers if temperature exceeds 8.0 Celsius
const unsigned long DOOR_TIMEOUT_MS = 20000; // Alarm triggers if door is open for 20,000 ms (20 seconds)
const unsigned long TEMP_READ_INTERVAL = 2000; // Read temperature every 2,000 ms (2 seconds)

// State Tracking Variables
unsigned long doorOpenedAt = 0;
unsigned long lastTempRead = 0;

bool isDoorOpen = false;
bool tempAlarmActive = false;
bool doorAlarmActive = false;
float currentTemp = 0.0;

void setup() {
    // Initialize serial communication for data logging
    Serial.begin(115200);

    // Configure pins
    // INPUT_PULLUP applies an internal 20k-50k ohm resistor to 5V.
    pinMode(REED_PIN, INPUT_PULLUP);
    pinMode(BUZZER_PIN, OUTPUT);

    Serial.println("--- Fridge Monitor Initialized ---");
    Serial.print("Temp Threshold: ");
    Serial.print(TEMP_THRESHOLD_C);
    Serial.println(" C");
    Serial.print("Door Timeout: ");
    Serial.print(DOOR_TIMEOUT_MS / 1000);
    Serial.println(" seconds");
}

void loop() {
    // Capture current time once per loop iteration
    unsigned long currentMillis = millis();

    // ---------------------------------------------------------
    // 1. Evaluate Door State
    // ---------------------------------------------------------
    // Switch is closed (LOW) when magnet is present (door closed).
    // Switch is open (HIGH) when magnet is removed (door open).
    bool currentDoorState = (digitalRead(REED_PIN) == HIGH);

    // Detect state change: Door just opened
    if (currentDoorState && !isDoorOpen) {
        isDoorOpen = true;
        doorOpenedAt = currentMillis;
        Serial.println("[EVENT] Door Opened.");
    } 
    // Detect state change: Door just closed
    else if (!currentDoorState && isDoorOpen) {
        isDoorOpen = false;
        doorAlarmActive = false;
        Serial.println("[EVENT] Door Closed.");
        noTone(BUZZER_PIN); // Silence any active door alarms
    }

    // Check if door has been open longer than the timeout
    if (isDoorOpen && (currentMillis - doorOpenedAt >= DOOR_TIMEOUT_MS)) {
        if (!doorAlarmActive) {
            Serial.println("[ALARM] Door Open Timeout Exceeded!");
            doorAlarmActive = true;
        }
    }

    // ---------------------------------------------------------
    // 2. Evaluate Temperature
    // ---------------------------------------------------------
    if (currentMillis - lastTempRead >= TEMP_READ_INTERVAL) {
        lastTempRead = currentMillis;

        // Read 10-bit ADC value (0-1023)
        int adcValue = analogRead(LM35_PIN);

        // Convert ADC value to voltage (5V reference)
        // Note: 1024.0 is the correct divisor for a 10-bit AVR ADC
        float voltage = (adcValue / 1024.0) * 5.0;

        // LM35 outputs 10mV (0.01V) per degree Celsius
        currentTemp = voltage / 0.01;

        Serial.print("[DATA] Current Temp: ");
        Serial.print(currentTemp);
        Serial.println(" C");

        // Trigger temperature alarm if threshold is exceeded
        if (currentTemp > TEMP_THRESHOLD_C) {
            if (!tempAlarmActive) {
                Serial.println("[ALARM] Temperature Critical!");
                tempAlarmActive = true;
            }
        } else {
            if (tempAlarmActive) {
                Serial.println("[RESOLVED] Temperature returned to normal.");
                tempAlarmActive = false;
                noTone(BUZZER_PIN); // Silence temp alarm
            }
        }
    }

    // ---------------------------------------------------------
    // 3. Actuate Alarms (Non-blocking Buzzer Logic)
    // ---------------------------------------------------------
    // Priority 1: Door Alarm (Fast high-pitched beep)
    if (doorAlarmActive) {
        // Toggle tone every 250ms
        if ((currentMillis / 250) % 2 == 0) {
            tone(BUZZER_PIN, 1200); // 1200 Hz
        } else {
            noTone(BUZZER_PIN);
        }
    } 
    // Priority 2: Temperature Alarm (Slow lower-pitched beep)
    // Only sound if the door alarm isn't already sounding
    else if (tempAlarmActive && !doorAlarmActive) {
        // Toggle tone every 1000ms
        if ((currentMillis / 1000) % 2 == 0) {
            tone(BUZZER_PIN, 600); // 600 Hz
        } else {
            noTone(BUZZER_PIN);
        }
    }
    // No alarms active
    else if (!doorAlarmActive && !tempAlarmActive) {
        noTone(BUZZER_PIN);
    }
}

Comandos de compilación y subida

Navegue hasta el directorio justo por encima de su carpeta FridgeAlarm en su terminal. Asegúrese de que su Arduino UNO R3 esté conectado a su computadora por USB. Identifique su puerto serie (por ejemplo, COM3 en Windows, /dev/ttyACM0 en Linux o /dev/cu.usbmodem14101 en macOS) y ejecute los siguientes comandos exactos para preparar el entorno, compilar y subir el sketch.

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

# Install the AVR core for Arduino UNO
arduino-cli core install arduino:avr

# Compile the sketch for the Arduino UNO R3
arduino-cli compile --fqbn arduino:avr:uno FridgeAlarm

# Upload the compiled code to the board (replace <PORT> with your actual port)
arduino-cli upload --fqbn arduino:avr:uno --port <PORT> FridgeAlarm

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 objetivo principal del prototipo descrito en el artículo?




Pregunta 2: ¿Qué problema busca evitar este sistema en el almacenamiento comercial y de laboratorio?




Pregunta 3: ¿Qué consecuencia negativa previene el sistema en entornos residenciales?




Pregunta 4: ¿Qué función de programación se utiliza para manejar múltiples sensores sin bloqueo de subprocesos?




Pregunta 5: ¿Qué tipo de sensor de temperatura se menciona en el resultado esperado?




Pregunta 6: ¿Con qué frecuencia el sistema consulta el sensor de temperatura?




Pregunta 7: ¿A dónde se transmiten los datos de temperatura para su registro?




Pregunta 8: ¿Qué componente se utiliza para detectar con precisión los estados de la puerta?




Pregunta 9: ¿Qué concepto educativo de hardware demuestra este proyecto?




Pregunta 10: Además del desperdicio de energía, ¿qué otro problema principal busca evitar el monitoreo de la temperatura interna?




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: luz nocturna solar con Arduino UNO

Caso práctico: luz nocturna solar con Arduino UNO — hero

Objetivo y caso de uso

Qué construirás: Construirás un controlador automático de luz nocturna solar que activa un arreglo de LED de alta potencia al anochecer. Utiliza un LDR para la detección de luz ambiental y un divisor de voltaje con resistencias para monitorear activamente y proteger una batería de 12V contra descargas profundas.

Por qué es importante / Casos de uso

  • Protección de la batería: Monitorea activamente el estado de la batería y corta la carga cuando el voltaje cae por debajo de los umbrales seguros (p. ej., < 10.5V), previniendo daños permanentes a las celdas de plomo-ácido o de iones de litio.
  • Eficiencia energética: Utiliza un LDR con histéresis por software para asegurar que la iluminación se active solo cuando sea realmente necesario, evitando el parpadeo rápido y el desperdicio de energía durante las horas del crepúsculo.
  • Automatización fuera de la red (Off-Grid): Forma la lógica autónoma central para la iluminación remota de paradas de autobús, cobertizos de jardín o luces de seguridad para senderos que operan de manera totalmente independiente de la red eléctrica.
  • Control de potencia escalable: Utiliza un MOSFET de canal N de nivel lógico para demostrar cómo un microcontrolador de bajo consumo de 5V puede conmutar de manera segura grandes cargas de CC (hasta 5A/60W).

Resultado esperado

  • Lectura precisa de voltaje: El Arduino escalará y medirá correctamente el voltaje de la batería de 12V a través del circuito divisor con un margen de error < 0.1V.
  • Actuación confiable: Conmuta de forma fluida la carga del LED de alta potencia con una latencia < 10ms una vez que el LDR detecta el umbral de oscuridad objetivo.
  • Preservación de la batería: Desconecta con éxito el arreglo de LED automáticamente cuando la capacidad de la batería cae al límite crítico de seguridad de 10.5V.

Audiencia: Desarrolladores de IoT, creadores de hardware y entusiastas de los sistemas fuera de la red; Nivel: Intermedio

Arquitectura/flujo: LDR & Divisor de voltaje (Sensores) → ADC de Arduino (Lógica/Procesamiento) → MOSFET de canal N (Interruptor) → Arreglo de LED de 12V (Salida)

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, 2 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 de bajo voltaje, no un producto certificado. Antes de encender la configuración, verifique el cableado de su Arduino UNO R3, evite cortocircuitar 5 V, GND o los pines digitales, desconecte la alimentación antes de cambiar las conexiones y use módulos de interfaz adecuados para relés, motores o cargas externas.

Diagrama de bloques conceptual

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

Arquitectura funcional

LDR & Divisor de voltaje (Sensores)

ADC de Arduino (Lógica/Procesamiento)

MOSFET de canal N (Interruptor)

Arreglo de LED de 12V (Salida)

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 la Ley de Ohm (específicamente los cálculos del divisor de voltaje), programación fundamental en C++ para Arduino (variables, declaraciones if/else, millis() para la temporización) y familiaridad con el funcionamiento de una interfaz de línea de comandos. Debe tener instalado y configurado Arduino CLI en su computadora host.

Materiales

Necesitará los siguientes componentes exactos para construir este prototipo:

  • Microcontrolador: Arduino UNO R3 (ATmega328P)
  • Sensor de luz: 1x LDR estándar (Fotorresistencia)
  • MOSFET: 1x IRLZ44N (MOSFET de potencia de canal N de nivel lógico)
  • Resistencias (1/4 de vatio):
    • 1x 30kΩ (Divisor de batería R1)
    • 2x 10kΩ (Divisor de batería R2, LDR pull-down)
    • 1x 10kΩ (Compuerta del MOSFET pull-down)
    • 1x 220Ω (Limitador de corriente de la compuerta del MOSFET)
  • Carga: 1x Arreglo de LED de 12V o tira de LED de 12V (Máximo 2 Amperios para pruebas en protoboard)
  • Fuente de alimentación: 1x Batería de 12V (p. ej., paquete de iones de litio 3S o batería de plomo-ácido sellada de 12V)
  • Prototipado: 1x Protoboard, cables puente macho a macho variados

Configuración/Conexión

Debido a que el Arduino UNO R3 funciona a 5V, no puede medir directamente una batería de 12V ni accionar una carga de 12V. Debemos utilizar circuitos de interfaz específicos.

1. El divisor de voltaje de la batería
El ADC de Arduino solo puede medir hasta 5V. Para medir una batería de 12V (que puede alcanzar hasta 14.4V durante la carga), usamos un divisor de voltaje. Conecte la resistencia de 30kΩ (R1) al terminal positivo de la batería (BAT+). Conecte el otro extremo de la resistencia de 30kΩ al pin A0 de Arduino. Conecte la resistencia de 10kΩ (R2) desde el pin A0 de Arduino a Tierra (GND).
Fórmula: Vout = Vin * (R2 / (R1 + R2)). A una entrada de 20V, A0 ve exactamente 5V. El factor de escala en el código será 4.0.

2. El divisor del LDR
Conecte una pata del LDR al pin de 5V de Arduino. Conecte la otra pata del LDR al pin A1 de Arduino. Conecte una resistencia de 10kΩ desde el pin A1 de Arduino a Tierra (GND).
Comportamiento: En luz brillante, la resistencia del LDR disminuye, acercando A1 a 5V. En la oscuridad, la resistencia del LDR aumenta y la resistencia de 10kΩ acerca A1 a 0V.

3. El controlador LED MOSFET
Sostenga el MOSFET IRLZ44N de modo que la pestaña de metal quede en la parte posterior y el texto lo mire a usted. Los pines de izquierda a derecha son Compuerta (Gate) (1), Drenador (Drain) (2) y Surtidor (Source) (3).
* Conecte el Pin 9 de Arduino a una resistencia de 220Ω, luego a la Compuerta del MOSFET.
* Conecte una resistencia de 10kΩ desde la Compuerta del MOSFET directamente a Tierra (esto evita que el LED parpadee mientras el Arduino arranca).
* Conecte el Surtidor del MOSFET a Tierra.
* Conecte el Drenador del MOSFET al cable negativo (Cátodo) de su arreglo de LED de 12V.
* Conecte el cable positivo (Ánodo) de su arreglo de LED de 12V directamente al terminal positivo de la batería (BAT+).

4. Tierra común
Asegúrese de que el GND del Arduino, el terminal negativo de la batería, el Surtidor del MOSFET y todas las conexiones a tierra del divisor estén unidas en el riel negativo de la protoboard.

Tabla de asignación de pines

Pin de Arduino UNO R3 Conexión del componente Función
A0 Unión de resistencias de 30kΩ y 10kΩ Detección de voltaje de la batería
A1 Unión del LDR y resistencia de 10kΩ Detección de luz ambiental
Pin 9 (PWM) Resistencia de 220Ω → Compuerta del MOSFET Brillo / Conmutación del LED
5V Pata superior del LDR Voltaje de referencia para el LDR
GND Riel de tierra común Retorno del sistema / Referencia
VIN No conectado en esta prueba Alimentar Arduino vía USB para pruebas

Código validado

La siguiente sección contiene dos archivos. El primero es el firmware en C++ para el Arduino UNO R3. El segundo es un script de Python opcional que se ejecuta en su computadora para registrar los datos en serie del Arduino en un archivo CSV, lo cual es muy útil para validar la curva de descarga de la batería y los puntos de conmutación.

Firmware de Arduino: solar_night_light.ino

Cree una carpeta llamada solar_night_light y coloque este código dentro de un archivo llamado solar_night_light.ino.

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

/*
 * Solar Night Light Controller
 * Target: Arduino UNO R3 (ATmega328P)
 * Purpose: Controls a 12V LED via MOSFET based on LDR light levels,
 *          incorporating Low Voltage Disconnect (LVD) for battery protection.
 */

// --- Hardware Pins ---
const int PIN_BATTERY_SENSE = A0;
const int PIN_LIGHT_SENSE   = A1;
const int PIN_LED_DRIVE     = 9;

// --- Calibration & Thresholds ---
// Voltage Divider: R1 = 30k, R2 = 10k. Ratio = (30+10)/10 = 4.0
const float VOLTAGE_DIVIDER_RATIO = 4.0;
const float ADC_REFERENCE_VOLTAGE = 5.0;
const float ADC_MAX_VALUE = 1023.0;

// Battery Thresholds (12V Lead-Acid / 3S Li-ion example)
const float BATT_LVD_DISCONNECT = 10.5; // Turn off below 10.5V
const float BATT_LVD_RECONNECT  = 11.5; // Require 11.5V to turn back on

// Light Thresholds (0-1023). Higher value = brighter ambient light.
const int LIGHT_THRESHOLD_DARK  = 300;  // Below this is considered night
const int LIGHT_THRESHOLD_LIGHT = 450;  // Above this is considered day

// --- System State ---
enum SystemState {
  STATE_DAYTIME,
  STATE_NIGHT_ON,
  STATE_LOW_BATTERY
};

SystemState currentState = STATE_DAYTIME;

// --- Timing ---
unsigned long lastLogTime = 0;
const unsigned long LOG_INTERVAL_MS = 1000;

void setup() {
  Serial.begin(115200);

  pinMode(PIN_BATTERY_SENSE, INPUT);
  pinMode(PIN_LIGHT_SENSE, INPUT);

  // Initialize MOSFET drive pin
  pinMode(PIN_LED_DRIVE, OUTPUT);
  analogWrite(PIN_LED_DRIVE, 0); // Ensure LED is OFF at boot

  Serial.println("Solar Night Light Controller Initialized.");
  Serial.println("Timestamp_ms,Battery_V,Light_ADC,State,PWM_Out");
}

void loop() {
  // 1. Read Sensors
  int rawBatteryADC = analogRead(PIN_BATTERY_SENSE);
  int rawLightADC = analogRead(PIN_LIGHT_SENSE);

  // 2. Calculate Real Voltage
  float pinVoltage = (rawBatteryADC / ADC_MAX_VALUE) * ADC_REFERENCE_VOLTAGE;
  float batteryVoltage = pinVoltage * VOLTAGE_DIVIDER_RATIO;

  // 3. State Machine Logic
  switch (currentState) {

    case STATE_DAYTIME:
      // Transition to NIGHT if it gets dark AND battery is healthy
      if (rawLightADC < LIGHT_THRESHOLD_DARK) {
        if (batteryVoltage > BATT_LVD_RECONNECT) {
          currentState = STATE_NIGHT_ON;
        } else {
          currentState = STATE_LOW_BATTERY; // Too dark, but battery is dead
        }
// ...

/*
 * Solar Night Light Controller
 * Target: Arduino UNO R3 (ATmega328P)
 * Purpose: Controls a 12V LED via MOSFET based on LDR light levels,
 *          incorporating Low Voltage Disconnect (LVD) for battery protection.
 */

// --- Hardware Pins ---
const int PIN_BATTERY_SENSE = A0;
const int PIN_LIGHT_SENSE   = A1;
const int PIN_LED_DRIVE     = 9;

// --- Calibration & Thresholds ---
// Voltage Divider: R1 = 30k, R2 = 10k. Ratio = (30+10)/10 = 4.0
const float VOLTAGE_DIVIDER_RATIO = 4.0;
const float ADC_REFERENCE_VOLTAGE = 5.0;
const float ADC_MAX_VALUE = 1023.0;

// Battery Thresholds (12V Lead-Acid / 3S Li-ion example)
const float BATT_LVD_DISCONNECT = 10.5; // Turn off below 10.5V
const float BATT_LVD_RECONNECT  = 11.5; // Require 11.5V to turn back on

// Light Thresholds (0-1023). Higher value = brighter ambient light.
const int LIGHT_THRESHOLD_DARK  = 300;  // Below this is considered night
const int LIGHT_THRESHOLD_LIGHT = 450;  // Above this is considered day

// --- System State ---
enum SystemState {
  STATE_DAYTIME,
  STATE_NIGHT_ON,
  STATE_LOW_BATTERY
};

SystemState currentState = STATE_DAYTIME;

// --- Timing ---
unsigned long lastLogTime = 0;
const unsigned long LOG_INTERVAL_MS = 1000;

void setup() {
  Serial.begin(115200);

  pinMode(PIN_BATTERY_SENSE, INPUT);
  pinMode(PIN_LIGHT_SENSE, INPUT);

  // Initialize MOSFET drive pin
  pinMode(PIN_LED_DRIVE, OUTPUT);
  analogWrite(PIN_LED_DRIVE, 0); // Ensure LED is OFF at boot

  Serial.println("Solar Night Light Controller Initialized.");
  Serial.println("Timestamp_ms,Battery_V,Light_ADC,State,PWM_Out");
}

void loop() {
  // 1. Read Sensors
  int rawBatteryADC = analogRead(PIN_BATTERY_SENSE);
  int rawLightADC = analogRead(PIN_LIGHT_SENSE);

  // 2. Calculate Real Voltage
  float pinVoltage = (rawBatteryADC / ADC_MAX_VALUE) * ADC_REFERENCE_VOLTAGE;
  float batteryVoltage = pinVoltage * VOLTAGE_DIVIDER_RATIO;

  // 3. State Machine Logic
  switch (currentState) {

    case STATE_DAYTIME:
      // Transition to NIGHT if it gets dark AND battery is healthy
      if (rawLightADC < LIGHT_THRESHOLD_DARK) {
        if (batteryVoltage > BATT_LVD_RECONNECT) {
          currentState = STATE_NIGHT_ON;
        } else {
          currentState = STATE_LOW_BATTERY; // Too dark, but battery is dead
        }
      }
      // Transition to LOW_BATTERY if voltage drops even during day
      if (batteryVoltage < BATT_LVD_DISCONNECT) {
        currentState = STATE_LOW_BATTERY;
      }
      break;

    case STATE_NIGHT_ON:
      // Transition back to DAYTIME if light returns
      if (rawLightADC > LIGHT_THRESHOLD_LIGHT) {
        currentState = STATE_DAYTIME;
      }
      // Transition to LOW_BATTERY if voltage drops below safe limit
      if (batteryVoltage < BATT_LVD_DISCONNECT) {
        currentState = STATE_LOW_BATTERY;
      }
      break;

    case STATE_LOW_BATTERY:
      // Only recover from Low Battery if voltage rises above reconnect threshold
      if (batteryVoltage > BATT_LVD_RECONNECT) {
        if (rawLightADC > LIGHT_THRESHOLD_LIGHT) {
          currentState = STATE_DAYTIME;
        } else {
          currentState = STATE_NIGHT_ON;
        }
      }
      break;
  }

  // 4. Actuate Outputs
  int pwmValue = 0;
  if (currentState == STATE_NIGHT_ON) {
    pwmValue = 255; // 100% duty cycle (Full brightness)
  }
  analogWrite(PIN_LED_DRIVE, pwmValue);

  // 5. Telemetry Logging (Non-blocking)
  unsigned long currentMillis = millis();
  if (currentMillis - lastLogTime >= LOG_INTERVAL_MS) {
    lastLogTime = currentMillis;

    Serial.print(currentMillis);
    Serial.print(",");
    Serial.print(batteryVoltage, 2);
    Serial.print(",");
    Serial.print(rawLightADC);
    Serial.print(",");

    if (currentState == STATE_DAYTIME) Serial.print("DAYTIME");
    else if (currentState == STATE_NIGHT_ON) Serial.print("NIGHT_ON");
    else if (currentState == STATE_LOW_BATTERY) Serial.print("LOW_BATT");

    Serial.print(",");
    Serial.println(pwmValue);
  }
}

Script registrador de puerto serie: serial_logger.py

Este script de Python lee la salida en serie en formato CSV del Arduino y la guarda en un archivo. Guarde esto en el mismo directorio que la carpeta de su proyecto de Arduino.

#!/usr/bin/env python3
"""
Serial Logger for Solar Night Light Controller
Reads CSV telemetry from Arduino and writes to 'telemetry.csv'
Requires: pip install pyserial
"""

import serial
import time
import argparse

def main():
    parser = argparse.ArgumentParser(description="Log Arduino Serial Data to CSV")
    parser.add_add_argument("--port", required=True, help="Serial port (e.g., COM3 or /dev/ttyACM0)")
    parser.add_argument("--baud", type=int, default=115200, help="Baud rate")
    args = parser.parse_args()

    try:
        ser = serial.Serial(args.port, args.baud, timeout=2)
        print(f"Connected to {args.port} at {args.baud} baud.")

        with open("telemetry.csv", "w") as f:
            while True:
                if ser.in_waiting > 0:
                    line = ser.readline().decode('utf-8').strip()
                    print(line)
                    if "Initialized" not in line:
                        f.write(line + "\n")
                        f.flush()

    except serial.SerialException as e:
        print(f"Error opening serial port: {e}")
    except KeyboardInterrupt:
        print("\nLogging stopped by user.")
    finally:
        if 'ser' in locals() and ser.is_open:
            ser.close()

if __name__ == "__main__":
    main()

Comandos de compilación/flasheo/ejecución

Use Arduino CLI para compilar y flashear el firmware en su Arduino UNO R3. Asegúrese de que su Arduino esté conectado a través de USB.

Tabla de comandos

Comando Propósito
arduino-cli core update-index Actualiza la lista de núcleos de Arduino disponibles
arduino-cli core install arduino:avr Instala el núcleo requerido para el UNO R3
arduino-cli compile --fqbn arduino:avr:uno Compila el sketch solar_night_light.ino
arduino-cli upload --fqbn arduino:avr:uno --port <PORT> Flashea el binario compilado en la placa

Flujo de trabajo

  1. Obra su terminal y navegue hasta el directorio que contiene la carpeta solar_night_light.
  2. Asegúrese de que su núcleo esté actualizado:
    arduino-cli core update-index
    arduino-cli core install arduino:avr
  3. Compile el firmware:
    arduino-cli compile --fqbn arduino:avr:uno solar_night_light
  4. Identifique su puerto (p. ej., COM3 en Windows o /dev/ttyACM0 en Linux).
  5. Suba el firmware:
    arduino-cli upload --fqbn arduino:avr:uno --port /dev/ttyACM0 solar_night_light
  6. Inicie el registrador de Python para monitorear la salida (reemplace el puerto con su puerto real):
    python3 serial_logger.py --port /dev/ttyACM0

Validación paso a paso

Realice estas comprobaciones para validar el funcionamiento lógico y eléctrico del controlador. Mantenga el registrador de Python en ejecución para poder observar el estado del sistema.

  1. Comprobación de calibración de voltaje del ADC
    • Acción: Conecte una fuente de voltaje conocida (p. ej., una batería de 12V o fuente de alimentación de banco) a la entrada del divisor de la batería. Mida el voltaje real de la batería con un multímetro digital (DMM).
    • Observación esperada: La columna Battery_V en la salida en serie debe coincidir con la lectura del DMM con una diferencia de ±0.2V.
    • Condición de aprobación: El Arduino escala con precisión la lectura analógica al voltaje del mundo real.
  2. Verificación del estado diurno
    • Acción: Ilumine el LDR directamente con una linterna o luz de habitación brillante. Asegúrese de que el voltaje de la batería esté por encima de 11.5V.
    • Observación esperada: La salida en serie reporta DAYTIME, PWM_Out es 0. El arreglo de LED de 12V permanece completamente apagado.
    • Condición de aprobación: El sistema identifica correctamente el día y desactiva la carga.
  3. Transición al anochecer e histéresis
    • Acción: Cubra lentamente el LDR con su mano para simular el anochecer.
    • Observación esperada: Light_ADC disminuye. Una vez que cae por debajo de 300, el estado cambia a NIGHT_ON y PWM_Out salta a 255. El arreglo de LED se enciende. Descubrir ligeramente el LDR (el ADC sube a 350) no apaga el LED.
    • Condición de aprobación: El LED se enciende en la oscuridad y la banda muerta (300 a 450) evita que el LED parpadee en el umbral exacto.
  4. Activación de desconexión por bajo voltaje (LVD)
    • Acción: Mientras está en el estado NIGHT_ON, simule una batería agotándose reduciendo el voltaje de entrada al divisor por debajo de 10.5V (usando una fuente de alimentación variable o cambiando temporalmente la resistencia de 30kΩ por una de mayor valor).
    • Observación esperada: El estado cambia inmediatamente a LOW_BATT. PWM_Out cae a 0. El arreglo de LED se apaga.
    • Condición de aprobación: El controlador protege con éxito la batería al cortar la carga.
  5. Bloqueo de reconexión del LVD
    • Acción: Aumente el voltaje de entrada ligeramente a 11.0V.
    • Observación esperada: El sistema permanece

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 objetivo principal del proyecto descrito en el artículo?




Pregunta 2: ¿Qué componente se utiliza para la detección de luz ambiental en el proyecto?




Pregunta 3: ¿Qué método se utiliza para monitorear activamente y proteger la batería de 12V?




Pregunta 4: ¿Por debajo de qué voltaje aproximado el sistema corta la carga para proteger la batería?




Pregunta 5: ¿Qué tipos de baterías se mencionan que son protegidas contra descargas profundas?




Pregunta 6: ¿Qué técnica de software se utiliza junto con el LDR para evitar el parpadeo rápido de la luz durante el crepúsculo?




Pregunta 7: ¿Qué tipo de iluminación activa el controlador al anochecer?




Pregunta 8: ¿Cuál es un caso de uso mencionado para este sistema fuera de la red (Off-Grid)?




Pregunta 9: ¿Cómo opera el sistema en relación con la red eléctrica?




Pregunta 10: ¿Qué problema principal previene el corte de carga cuando el voltaje de la batería cae?




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 de bomba de achique con Arduino UNO

Caso práctico: alarma de bomba de achique con Arduino UNO — hero

Objetivo y caso de uso

Qué construirás: Un prototipo de controlador de bomba de sumidero de doble flotador que automatiza un relé de 5V según los niveles de agua y activa una alarma de zumbador piezoeléctrico si el agua permanece peligrosamente alta.

Por qué es importante / Casos de uso

  • Prevención de inundaciones en sótanos: Enciende automáticamente una bomba antes de que el agua desborde el pozo del sumidero, previniendo costosos daños por agua.
  • Monitorización de tanques industriales: Mantiene los niveles de fluido dentro de una banda de operación segura específica sin que el hardware de la bomba haga ciclos cortos.
  • Riego agrícola: Automatiza el llenado o vaciado de abrevaderos de ganado o depósitos hidropónicos.
  • Teoría de control educativa: Introduce la histéresis de hardware, utilizando dos umbrales diferentes para prevenir una conmutación mecánica rápida y dañina, y extender la vida útil del relé.

Resultado esperado

  • El relé de 5V se activa (bomba ENCENDIDA) solo cuando el agua levanta el interruptor de flotador Alto.
  • El relé permanece activo incluso cuando el agua cae por debajo del flotador Alto, desactivándose solo cuando el flotador Bajo cae (bomba APAGADA).
  • Si el flotador Alto permanece activado durante más de 5 segundos, un zumbador piezoeléctrico emite una alarma audible que indica un posible fallo en la bomba.

Audiencia: Desarrolladores de IoT, creadores de hardware y entusiastas de la domótica; Nivel: Intermedio

Arquitectura/flujo: Interruptores de flotador dobles (Entradas digitales) → Microcontrolador (Lógica de histéresis & Temporizador de 5s) → Relé de 5V (Control de bomba) & Zumbador piezoeléctrico (Alarma)

Nota educativa de validación

Antes de publicar este caso, el contenido pasó la puerta automática de validación de Prometeo con estado PASS. El validador comprobó los bloques de código, la estructura del artículo, los comandos copiables y la coherencia con el catálogo de dispositivos soportados.

Evidencia de validación publicada

  • Resultado automático: PASS.
  • Estructura parseada: 3 apartados, 3 tablas y 2 bloques de código detectados antes de publicar.
  • Código comprobado: 2 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 de bajo voltaje, no un producto certificado. Antes de encender la configuración, verifique el cableado de su Arduino UNO R3, evite cortocircuitar 5 V, GND o los pines digitales, desconecte la alimentación antes de cambiar las conexiones y use módulos de interfaz adecuados para relés, motores o cargas externas.

Diagrama de bloques conceptual

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

Arquitectura funcional

Interruptores de flotador dobles (Entrada…

Microcontrolador

Relé de 5V (Control de bomba) & Zumbador…

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 tutorial, debes tener:
* Una comprensión básica de la lógica digital (estados HIGH/LOW).
* Familiaridad con el concepto de resistencias pull-up (específicamente el INPUT_PULLUP interno del Arduino).
* Un ordenador con la interfaz de línea de comandos (Terminal/Símbolo del sistema) disponible.
* Arduino CLI instalado y añadido al PATH de tu sistema.

Materiales

Para construir este prototipo específico, necesitarás los siguientes componentes exactos:
* Microcontrolador: Arduino UNO R3 (ATmega328P)
* Sensores: 2x Interruptores mecánicos de flotador (verticales u horizontales, tipo interruptor de láminas magnético estándar)
* Actuador: 1x Módulo de relé de 5 V (estándar de 1 canal, optoaislado)
* Alarma: 1x Zumbador piezoeléctrico (se prefiere un zumbador activo de 5V, aunque el pasivo funciona con la función tone() utilizada en este código)
* Prototipado: 1x Placa de pruebas (breadboard) sin soldadura
* Cableado: Surtido de cables puente macho-macho y macho-hembra
* Alimentación: Cable USB estándar A a B para programar y alimentar el Arduino

Configuración/Conexión

Este proyecto se basa en las resistencias pull-up internas del Arduino para los interruptores de flotador. Esto simplifica el cableado al eliminar la necesidad de resistencias externas. Cuando el interruptor de flotador está abierto, el pin del Arduino lee HIGH. Cuando el agua sube y cierra el interruptor de flotador, conecta el pin a Tierra (Ground), y el Arduino lee LOW.

La mayoría de los módulos de relé estándar de 5 V son «Activos en BAJO» (Active LOW), lo que significa que llevar el pin de señal a LOW energiza la bobina del relé, y ponerlo en HIGH lo apaga. El código proporcionado tiene en cuenta este comportamiento común.

Tabla de asignación de pines

Componente Pin Arduino UNO R3 Conexión del componente Notas
Interruptor de flotador Bajo Pin digital 2 Cable 1 El cable 2 va al GND del Arduino
Interruptor de flotador Alto Pin digital 3 Cable 1 El cable 2 va al GND del Arduino
Módulo de relé de 5 V Pin digital 4 IN / Señal VCC a 5V, GND a GND
Zumbador piezoeléctrico Pin digital 5 Positivo (+) Negativo (-) al GND del Arduino

Nota sobre la orientación del interruptor de flotador: Los interruptores mecánicos de flotador generalmente contienen un interruptor de láminas magnético y un anillo flotante. A menudo puedes quitar el clip en C en la parte inferior para voltear el anillo del flotador. Para este proyecto, configura ambos interruptores para que estén Abiertos en reposo (abajo) y Cerrados cuando floten (arriba).

Código validado

El proyecto utiliza dos archivos de código separados. El primero es una prueba de hardware para asegurar que tu relé y zumbador estén cableados correctamente. El segundo es la lógica principal del controlador.

Sketch de prueba de hardware (hardware_test.ino)

Crea un directorio llamado hardware_test y guarda el siguiente código como hardware_test.ino dentro de él. Este sketch alterna el relé y el zumbador secuencialmente para que puedas verificar tu cableado antes de añadir la complejidad de los sensores de flotador.

/*
 * hardware_test.ino
 * Basic validation sketch to verify Relay and Buzzer connections.
 */

const int RELAY_PIN = 4;
const int BUZZER_PIN = 5;

// Most 5V relay modules are Active LOW.
const int RELAY_ON = LOW;
const int RELAY_OFF = HIGH;

void setup() {
  Serial.begin(9600);
  Serial.println("Starting Hardware Test...");

  pinMode(RELAY_PIN, OUTPUT);
  pinMode(BUZZER_PIN, OUTPUT);

  // Ensure relay and buzzer are OFF initially
  digitalWrite(RELAY_PIN, RELAY_OFF);
  digitalWrite(BUZZER_PIN, LOW);
}

void loop() {
  Serial.println("Testing Relay: ON");
  digitalWrite(RELAY_PIN, RELAY_ON);
  delay(2000);

  Serial.println("Testing Relay: OFF");
  digitalWrite(RELAY_PIN, RELAY_OFF);
  delay(2000);

  Serial.println("Testing Buzzer: ON");
  tone(BUZZER_PIN, 1000); // Play 1kHz tone
  delay(1000);

  Serial.println("Testing Buzzer: OFF");
  noTone(BUZZER_PIN);
  delay(2000);

  Serial.println("Hardware test cycle complete. Repeating...");
  Serial.println("----------------------------------------");
}

Lógica principal del controlador (sump_controller.ino)

Crea un directorio llamado sump_controller y guarda el siguiente código como sump_controller.ino. Este código implementa la lógica de histéresis y el temporizador de alarma sin bloqueo.

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

/*
 * sump_controller.ino
 * Dual-float sump pump controller with hysteresis and high-water alarm.
 * Target: Arduino UNO R3 (ATmega328P)
 */

// --- Pin Definitions ---
const int LOW_FLOAT_PIN = 2;
const int HIGH_FLOAT_PIN = 3;
const int RELAY_PIN = 4;
const int BUZZER_PIN = 5;

// --- Logic Definitions ---
// Using INPUT_PULLUP: Switch closed (floating up) connects to GND (LOW)
const int FLOAT_TRIGGERED = LOW;
const int FLOAT_RESTING = HIGH;

// Standard 5V relay modules are typically Active LOW
const int RELAY_ON = LOW;
const int RELAY_OFF = HIGH;

// --- Alarm Configuration ---
// Time in milliseconds before triggering alarm if High Float stays triggered
const unsigned long ALARM_DELAY_MS = 5000; 

// --- State Variables ---
bool pumpIsActive = false;
bool alarmIsActive = false;
unsigned long highFloatStartTime = 0;

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

  Serial.println("Initializing Sump Pump Controller...");

  // Configure float switch pins with internal pull-ups
  pinMode(LOW_FLOAT_PIN, INPUT_PULLUP);
  pinMode(HIGH_FLOAT_PIN, INPUT_PULLUP);

  // Configure output pins
  pinMode(RELAY_PIN, OUTPUT);
  pinMode(BUZZER_PIN, OUTPUT);

  // Set initial safe states
  digitalWrite(RELAY_PIN, RELAY_OFF);
  noTone(BUZZER_PIN);

  Serial.println("System Ready.");
  Serial.println("-----------------------------------");
}
// ...

/*
 * sump_controller.ino
 * Dual-float sump pump controller with hysteresis and high-water alarm.
 * Target: Arduino UNO R3 (ATmega328P)
 */

// --- Pin Definitions ---
const int LOW_FLOAT_PIN = 2;
const int HIGH_FLOAT_PIN = 3;
const int RELAY_PIN = 4;
const int BUZZER_PIN = 5;

// --- Logic Definitions ---
// Using INPUT_PULLUP: Switch closed (floating up) connects to GND (LOW)
const int FLOAT_TRIGGERED = LOW;
const int FLOAT_RESTING = HIGH;

// Standard 5V relay modules are typically Active LOW
const int RELAY_ON = LOW;
const int RELAY_OFF = HIGH;

// --- Alarm Configuration ---
// Time in milliseconds before triggering alarm if High Float stays triggered
const unsigned long ALARM_DELAY_MS = 5000; 

// --- State Variables ---
bool pumpIsActive = false;
bool alarmIsActive = false;
unsigned long highFloatStartTime = 0;

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

  Serial.println("Initializing Sump Pump Controller...");

  // Configure float switch pins with internal pull-ups
  pinMode(LOW_FLOAT_PIN, INPUT_PULLUP);
  pinMode(HIGH_FLOAT_PIN, INPUT_PULLUP);

  // Configure output pins
  pinMode(RELAY_PIN, OUTPUT);
  pinMode(BUZZER_PIN, OUTPUT);

  // Set initial safe states
  digitalWrite(RELAY_PIN, RELAY_OFF);
  noTone(BUZZER_PIN);

  Serial.println("System Ready.");
  Serial.println("-----------------------------------");
}

void loop() {
  // 1. Read the current state of both float switches
  int lowFloatState = digitalRead(LOW_FLOAT_PIN);
  int highFloatState = digitalRead(HIGH_FLOAT_PIN);

  // 2. Evaluate Hysteresis Logic for the Pump
  if (highFloatState == FLOAT_TRIGGERED) {
    // Water has reached the top float. Turn pump ON.
    if (!pumpIsActive) {
      pumpIsActive = true;
      digitalWrite(RELAY_PIN, RELAY_ON);
      Serial.println("ACTION: High level reached. Pump turned ON.");
    }
  } 
  else if (lowFloatState == FLOAT_RESTING) {
    // Water has dropped below the bottom float. Turn pump OFF.
    if (pumpIsActive) {
      pumpIsActive = false;
      digitalWrite(RELAY_PIN, RELAY_OFF);
      Serial.println("ACTION: Low level cleared. Pump turned OFF.");
    }
  }
  // Note: If water is between the two floats, pumpIsActive retains its previous state.
  // This is the core of hysteresis.

  // 3. Evaluate Alarm Logic (Non-blocking timer)
  if (highFloatState == FLOAT_TRIGGERED) {
    // If this is the first moment we see the high float triggered, record the time
    if (highFloatStartTime == 0) {
      highFloatStartTime = millis();
    }

    // Check if the high float has been triggered longer than the allowed delay
    if ((millis() - highFloatStartTime >= ALARM_DELAY_MS) && !alarmIsActive) {
      alarmIsActive = true;
      tone(BUZZER_PIN, 2000); // 2kHz warning tone
      Serial.println("ALARM: Water level critically high! Pump may be failing.");
    }
  } else {
    // Water is no longer at the high float. Reset alarm states.
    if (highFloatStartTime != 0 || alarmIsActive) {
      highFloatStartTime = 0;
      alarmIsActive = false;
      noTone(BUZZER_PIN);
      Serial.println("STATUS: High water condition cleared. Alarm reset.");
    }
  }

  // Small delay to debounce switch chattering from simulated water ripples
  delay(100); 
}

Entendiendo la lógica del código

El sketch del controlador principal se basa en dos conceptos fundamentales de ingeniería:

  1. Histéresis: Si solo usáramos un interruptor de flotador, las ondulaciones en el agua harían que el interruptor rebotara rápidamente entre ENCENDIDO y APAGADO. Este «ciclo corto» destruye los relés mecánicos y los motores de las bombas. Al usar un flotador Alto para encender la bomba y un flotador Bajo completamente separado para apagar la bomba, el nivel del agua debe recorrer la distancia física entre los dos interruptores antes de que el estado cambie.
  2. Temporizadores sin bloqueo: En lugar de usar delay(5000) para la alarma —lo cual congelaría todo el Arduino y detendría la comprobación de los flotadores—, el código usa millis(). Registra una marca de tiempo cuando se activa el flotador alto (highFloatStartTime) y la resta continuamente del tiempo actual. Si la diferencia supera los 5000 milisegundos, suena la alarma, todo mientras el Arduino continúa evaluando la lógica de la bomba 10 veces por segundo.

Comandos de Compilación/Flasheo/Ejecución

Usa Arduino CLI para compilar y subir el código a tu Arduino UNO R3.

Tabla de referencia de comandos

Tarea Comando
Actualizar índice arduino-cli core update-index
Instalar núcleo AVR arduino-cli core install arduino:avr
Compilar código arduino-cli compile --fqbn arduino:avr:uno sump_controller
Subir a la placa arduino-cli upload --fqbn arduino:avr:uno --port <PORT> sump_controller
Monitor serie arduino-cli monitor --port <PORT> --config baudrate=9600

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

Flujo de ejecución

  1. Abre tu terminal y navega hasta el directorio que contiene tu carpeta sump_controller.
  2. Actualiza tu índice de núcleos y asegúrate de que la arquitectura AVR esté instalada (los dos primeros comandos de la tabla).
  3. Compila el sketch para verificar que no haya errores de sintaxis.
  4. Conecta tu Arduino UNO R3 vía USB.
  5. Sube el código compilado a la placa.
  6. Inicia el monitor serie para ver los registros del sistema y comenzar la validación física.

Validación paso a paso

Con el monitor serie abierto, manipula manualmente los interruptores de flotador físicos para simular la subida y bajada del agua.

  1. Estado inicial (Pozo vacío)
    • Acción: Deja ambos flotadores colgando hacia abajo (en reposo/abiertos).
    • Observación esperada: El monitor serie muestra «System Ready.» El relé está en silencio. El zumbador está en silencio.
    • Condición de aprobación: El pin 4 está en HIGH (Relé APAGADO), el pin 5 está en LOW.
  2. Agua subiendo (Nivel medio)
    • Acción: Levanta el flotador Bajo (cerrado). Deja el flotador Alto abajo.
    • Observación esperada: No hay cambios en el relé ni en el zumbador.
    • Condición de aprobación: El sistema espera al flotador Alto. La histéresis previene la activación prematura.
  3. Agua subiendo (Nivel alto / Activación de la bomba)
    • Acción: Mantén el flotador Bajo arriba y levanta el flotador Alto.
    • Observación esperada: Un «clic» distintivo del módulo de relé. El monitor serie registra «ACTION: High level reached. Pump turned ON.»
    • Condición de aprobación: El LED indicador del relé se enciende. La lógica de la bomba se activa con éxito.
  4. Disparador de alarma (Simulación de fallo de la bomba)
    • Acción: Mantén ambos flotadores arriba durante más de 5 segundos.
    • Observación esperada: Después de exactamente 5 segundos, el zumbador piezoeléctrico emite un tono fuerte de 2kHz. El monitor serie registra «ALARM: Water level critically high!».
    • Condición de aprobación: El temporizador sin bloqueo evalúa con éxito el tiempo transcurrido y activa la advertencia.
  5. Agua retrocediendo (Nivel medio)
    • Acción: Deja caer el flotador Alto (abierto). Mantén el flotador Bajo arriba.
    • Observación esperada: El zumbador se detiene inmediatamente. El monitor serie registra «STATUS: High water condition cleared.» Crucialmente, el relé permanece ENCENDIDO (con clic).
    • Condición de aprobación: La lógica de histéresis mantiene la bomba activa para continuar drenando el pozo.
  6. Agua retrocediendo (Pozo vacío)
    • Acción: Deja caer el flotador Bajo (abierto).
    • Observación esperada: El relé hace «clic» para apagarse. El monitor serie registra «ACTION: Low level cleared. Pump turned OFF.»
    • Condición de aprobación: El sistema regresa al estado seguro inicial.

Solución de problemas

Síntoma Causa probable Solución
El relé se enciende cuando los flotadores están ABAJO, y se apaga cuando están ARRIBA El clip en C del interruptor de flotador está invertido (Normalmente Cerrado en lugar de Normalmente Abierto). Quita el clip en C inferior del flotador, voltea el anillo magnético al revés y vuelve a colocar el clip.
El LED del relé se enciende, pero no se escucha ningún «clic» Energía insuficiente para la bobina del relé. Asegúrate de que el VCC del módulo de relé esté conectado al pin de 5V del Arduino, no al pin de 3.3V.
La bomba hace ciclos cortos rápidamente (El relé repiquetea) Los interruptores de flotador están cableados al revés en los pines del Arduino. Intercambia los cables en los pines D

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 se describe en el artículo?




Pregunta 2: ¿Qué componente principal se automatiza según los niveles de agua?




Pregunta 3: ¿Qué acción toma el sistema si el agua permanece en un nivel peligrosamente alto?




Pregunta 4: ¿Cuál es el beneficio principal del sistema en sótanos?




Pregunta 5: En aplicaciones industriales, ¿qué problema evita el uso de este controlador?




Pregunta 6: ¿Qué uso agrícola se menciona para este prototipo?




Pregunta 7: ¿Qué concepto de teoría de control educativo introduce este proyecto?




Pregunta 8: ¿Por qué es importante utilizar dos umbrales diferentes (histéresis) en este sistema?




Pregunta 9: Según el resultado esperado, ¿cuándo se enciende exactamente la bomba (relé activado)?




Pregunta 10: ¿Qué sucede con el relé cuando el nivel del agua cae justo por debajo del flotador Alto?




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: