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
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
pyserialinstalada 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
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.




