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 MAX485Pin del Arduino UNOFunción / Descripción
VCC5VFuente de alimentación para el transceptor.
GNDGNDReferencia de tierra común.
RO (Receiver Out)Pin Digital 10Se conecta al RX de Software del Arduino.
RE (Receiver Enable)Pin Digital 2Activo en BAJO. Unido a DE.
DE (Driver Enable)Pin Digital 2Activo en ALTO. Unido a RE.
DI (Driver In)Pin Digital 11Se conecta al TX de Software del Arduino.
AUSB-RS485 ‘A’Línea de bus RS485 no inversora.
BUSB-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 UNOFunción / Descripción
VCC / DC+5VFuente de alimentación para la bobina del relé y el optoacoplador.
GND / DC-GNDReferencia de tierra común.
IN / SignalPin Digital 7Señ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);
    }
// ...

🔒 Este contenido es premium. Con la suscripción mensual (7 días gratis) podrás desbloquear el material didáctico completo y el pack PDF listo para imprimir.🔓 Desbloquéalo — 7 días gratis
/*
 * 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).")
# ...

🔒 Este contenido es premium. Con la suscripción mensual (7 días gratis) podrás desbloquear el material didáctico completo y el pack PDF listo para imprimir.🔓 Desbloquéalo — 7 días gratis
#!/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.

TareaComando
Actualizar índice del núcleoarduino-cli core update-index
Instalar núcleo AVRarduino-cli core install arduino:avr
Compilar sketcharduino-cli compile --fqbn arduino:avr:uno rs485_relay_node
Subir a la placaarduino-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:
Scroll al inicio