View this PageEdit this PageUploads to this PageVersions of this PageHomeRecent ChangesSearchHelp Guide

Conexión con RS-422 y comunicación entre dispositivos, ejemplos

Datos previos:

En el último enlace ponen estos ejemplos del "wiring" necesario para comunicación simplex, half-duplex y full-duplex:

https://naylampmechatronics.com/blog/37_comunicacion-rs485-con-arduino.html
==CUT===
...

Usar el módulo RS485 como transmisor


En esta configuración el modulo solo trabaja como transmisor, para que el modulo sepa que las salida A B se van a comportar como salida de datos, se tiene que conectar a 5V los pines RE y DE. Desde el Arduino se envían los datos hacia el pin DI (Data Input) del módulo y este transmitirá los datos por los pines AB

Uploaded Image: Modulo RS485 como transmisor.jpg
Imagen: Modulo RS485 como transmisor

Usar el módulo RS485 como receptor


Al conectar los pines RE y DE el modulo se comporta como Receptor, y los datos recibidos por AB estarán presentes en el pin RO(Receiver Output), conectando el pin RO del módulo al RX de nuestro Arduino podemos leer los datos recibidos.

Uploaded Image: Modulo RS485 como receptor.jpg
Imagen: Modulo RS485 como receptor

Ejemplos

Ej 1. Comunicación Simplex entre dos Arduinos por RS485

Una comunicación simplex es una comunicación unidireccional, en este caso un Arduino se comporta solo como transmisor y el otro solo como receptor, a nivel de programación es como si estuviéramos trabajando con una comunicación serial, pero en un solo sentido. Uno envía y el otro solo recibe datos.

Veamos un ejemplo:

Desde un Arduino a través de un potenciómetro moveremos un servomotor que estará conectado en otro Arduino, solo dos cables (salidas A y B del RS485) unirán a los Arduinos, si la distancia es larga se recomienda usar cable trenzado.


Uploaded Image: Arduino RS485 simplex o unidereccional.jpg
Imagen: Arduino RS485 simplex o unidereccional


El código del transmisor es el siguiente:

void setup() 
{ 
  Serial.begin(9600);
} 
 
void loop() 
{ 
  int lectura = analogRead(0);//leemos el valor del potenciómetro (de 0 a 1023) 
  byte angulo= map(lectura, 0, 1023, 0, 180);     // escalamos la lectura a un valor de ángulo (entre 0 y 180) 
  Serial.write(angulo); //enviamos el ángulo correspondiente
  delay(50);                           
} 


El código del Arduino receptor es el siguiente:

#include <Servo.h> 
 
Servo myservo;  // creamos el objeto servo 
 
void setup() 
{ 
  Serial.begin(9600);  
  myservo.attach(9);  // asignamos el pin 9 para el servo.
} 
 
void loop() 
{ 
  
  if (Serial.available()) {
    int angulo = Serial.read(); //Leemos el dato recibido 
    if(angulo<=180) //verificamos que sea un valor en el rango del servo
    {
      myservo.write(angulo); //movemos el servomotor al ángulo correspondiente.
    }
  }
} 



Como se observa es una simple comunicación serial, en el Arduino transmisor se hace la lectura del potenciómetro se lo escala y se lo envía serialmente, en el receptor recibimos el dato y movemos el servomotor.

De esta forma podemos realizar una comunicación entre dos Arduino pero en una sola dirección, para hacerlo en dos direcciones tenemos dos formas, half-duplex y full-duplex


Ej 2. Comunicación full dúplex entre dos Arduinos

En este caso necesitamos agregar otro par de líneas más, en total unirían a los Arduino 4 líneas, un par son para transmitir (TX) y otro par para recibir (RX).

En el siguiente ejemplo desde un Arduino no solo enviaremos datos para mover un servomotor sino también recibiremos datos de un sensor, usaremos un potenciómetro para simular el sensor.

Las conexiones serían las siguientes:

Uploaded Image: Arduino RS485 full duplex.jpg
Imagen: Arduino RS485 full duplex

Al Arduino de la izquierda, lo llamaremos Maestro, pues es el Arduino principal y quien administrara todas las órdenes, mientras que el segundo Arduino lo denominaremos Esclavo; esta no es específicamente la definición de Maestro/Esclavo en una comunicación RS485 pero usaremos estas denominaciones para saber a qué Arduino nos estamos refiriendo.

El código del Arduino Maestro.


const int ledPin =  13;      // Numero del pin para el Led
void setup() 
{ 
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);//inicializamos el pin del Led como salida
} 
 
void loop() 
{ 
  if(Serial.available())
  {
    if(Serial.read()=='i') //si recibimos el inicio de trama
    {
       int dato=Serial.parseInt(); //recibimos valor numérico
       if(Serial.read()=='f') //Si el fin de trama es el correcto
       {
         funcion(dato);  //Realizamos la acción correspondiente  
       }
    }
  }
  
  int lectura = analogRead(0);//leemos el valor del potenciómetro (de 0 a 1023) 
  int angulo= map(lectura, 0, 1023, 0, 180); // escalamos la lectura a un valor de ángulo (entre 0 y 180) 
  //---enviamos el ángulo para mover el servo------
  Serial.print("I"); //inicio de trama
  Serial.print("S"); //S para indicarle que vamos a mover el servo
  Serial.print(angulo); //ángulo  o dato
  Serial.print("F"); //fin de trama
  //----------------------------
  delay(50); 
  //---solicitamos una lectura del sensor----------
  Serial.print("I"); //inicio de trama
  Serial.print("L"); //L para indicarle que vamos a Leer el sensor
  Serial.print("F"); //fin de trama
  //------------------------------------------------
  delay(50); 
  
} 

//esta función  puede variar de acuerdo a su necesidad
void funcion(int dato) 
{
  if(dato>500)
  digitalWrite(ledPin, HIGH); 
  else
  digitalWrite(ledPin, LOW); 
}




Código del Arduino Esclavo:


#include <Servo.h> 
 
Servo myservo;  // creamos el objeto servo 
 
void setup() 
{ 
  Serial.begin(9600);  
  myservo.attach(9);  // asignamos el pin 9 para el servo.
} 
 
void loop() 
{ 
  if(Serial.available()>0)
  {
    if(Serial.read()=='I') //Si recibimos el inicio de trama
    {
      char funcion=Serial.read(); //leemos el carácter de función
      //---Si el carácter de función es una S entonces la trama es para mover el motor----------- 
      if(funcion=='S') 
       {
           int angulo=Serial.parseInt(); //recibimos el ángulo
           if(Serial.read()=='F') //Si el fin de trama es el correcto
           {
             if(angulo<=180) //verificamos que sea un valor en el rango del servo
              {
                myservo.write(angulo); //movemos el servomotor al ángulo correspondiente.
              }   
           }
       }
       //---Si el carácter de función  es L entonces el maestro está solicitando una lectura del sensor
       else if(funcion=='L')
       {
          if(Serial.read()=='F') //Si el fin de trama es el correcto
           {
             int lectura = analogRead(0); //realizamos  la lectura del sensor
             //----enviamos la respuesta-----
             Serial.print("i"); //inicio de trama
             Serial.print(lectura); //valor del sensor
             Serial.print("f"); //fin de trama   
             //-----------------             
           }
       }
    }
  }
  delay(10);
} 



Como se observa se ha establecido una trama para la comunicación:

 [Inicio de trama][Función][Valor][Fin de trama]

En nuestro caso el inicio de trama es el carácter ‘A’ , la función es el carácter S o L para indicar que vamos a mover el servo o solicitar una lectura del sensor, el [valor] solo estará presente cuando la función necesite enviar una dato, y el fin de trama que usamos es el carácter F. Unos ejemplos de esta trama serian: “IS90F”,”IS120F”,”ILF”, etc.

El esclavo interpreta esta trama y realiza la función correspondiente, si es una función que necesite responder, la trama de respuesta es:

 [Inicio de trama][Valor][Fin de trama] , como por ejemplo “i865f”, “i64f”


Ej 3. Comunicación half dúplex entre dos Arduinos

En una comunicación half dúplex utiliza un solo canal para comunicarse, en un momento por el canal se transmiten datos y en otro momento se reciben datos, pero nunca podremos transmitir y recibir a la vez.

Para realizar esta comunicación los pines DE y RE del módulo RS485 deben ir conectados al Arduino, con esto desde el programa podemos establecer al módulo como transmisor o receptor


Uploaded Image: conexion Modulo RS485 o max485.jpg
Imagen: conexion Modulo RS485 o max485


El siguiente ejemplo hace lo mismo que el ejemplo anterior solo que esta vez se usa un módulo rs485 por Arduino y un par de cables para comunicarse.



Las conexiones serían las siguientes:

Uploaded Image: Arduino RS485 half duplex punto a punto.jpg
Imagen: Arduino RS485 half duplex punto a punto

Al igual que en el caso anterior el Arduino de la izquierda será el maestro y el de la derecha será el esclavo.



Código del Maestro:

const int ledPin =  13;  // Numero del pin para el Led
const int EnTxPin =  2;  // HIGH:TX y LOW:RX
void setup() 
{ 
  Serial.begin(9600);
  Serial.setTimeout(100);//establecemos un tiempo de espera de 100ms
  //inicializamos los pines
  pinMode(ledPin, OUTPUT);
  pinMode(EnTxPin, OUTPUT);
  digitalWrite(ledPin, LOW); 
  digitalWrite(EnTxPin, HIGH); 
} 
 
void loop() 
{ 
   
  int lectura = analogRead(0);//leemos el valor del potenciómetro (de 0 a 1023) 
  int angulo= map(lectura, 0, 1023, 0, 180);// escalamos la lectura a un valor de ángulo (entre 0 y 180) 
  //---enviamos el ángulo para mover el servo------
  Serial.print("I"); //inicio de trama
  Serial.print("S"); //S para indicarle que vamos a mover el servo
  Serial.print(angulo); //ángulo  o dato
  Serial.print("F"); //fin de trama
  //----------------------------
  delay(50); 
  //---solicitamos una lectura del sensor----------
  Serial.print("I"); //inicio de trama
  Serial.print("L"); //L para indicarle que vamos a Leer el sensor
  Serial.print("F"); //fin de trama
  Serial.flush();    //Esperamos hasta que se envíen los datos
  //----Leemos la respuesta del Esclavo-----
  digitalWrite(EnTxPin, LOW); //RS485 como receptor
  if(Serial.find("i"))//esperamos el inicio de trama
  {
      int dato=Serial.parseInt(); //recibimos valor numérico
      if(Serial.read()=='f') //Si el fin de trama es el correcto
       {
         funcion(dato);  //Realizamos la acción correspondiente          
      }
      
  }
  digitalWrite(EnTxPin, HIGH); //RS485 como Transmisor
  //----------fin de la respuesta-----------
  
} 
void funcion(int dato)
{
  if(dato>500)
  digitalWrite(ledPin, HIGH); 
  else
  digitalWrite(ledPin, LOW); 
}



Código de Esclavo:

#include <Servo.h> 
 
Servo myservo;  // creamos el objeto servo 
const int EnTxPin =  2; 
void setup() 
{ 
  Serial.begin(9600);  
  myservo.attach(9);  // asignamos el pin 9 para el servo.
  pinMode(EnTxPin, OUTPUT);
  digitalWrite(EnTxPin, LOW); //RS485 como receptor
} 
 
void loop() 
{ 
  if(Serial.available())
  {
    if(Serial.read()=='I') //Si recibimos el inicio de trama
    {

      char funcion=Serial.read();//leemos el carácter de función
      //---Si el carácter de función es una S entonces la trama es para mover el motor----------- 
      if(funcion=='S') 
       {
           int angulo=Serial.parseInt(); //recibimos el ángulo
           if(Serial.read()=='F') //Si el fin de trama es el correcto
           {
             if(angulo<=180) //verificamos que sea un valor en el rango del servo
              {
                myservo.write(angulo); //movemos el servomotor al ángulo correspondiente.
              }   
           }
       }
       //---Si el carácter de función  es L entonces el maestro está solicitando una lectura del sensor---
       else if(funcion=='L')
       {
          if(Serial.read()=='F') //Si el fin de trama es el correcto
           {
             int lectura = analogRead(0);  //realizamos  la lectura del sensor   
             digitalWrite(EnTxPin, HIGH);  //rs485 como transmisor
             Serial.print("i"); //inicio de trama            
             Serial.print(lectura); //valor del sensor
             Serial.print("f"); //fin de trama  
             Serial.flush(); //Esperamos hasta que se envíen los datos
             digitalWrite(EnTxPin, LOW); //RS485 como receptor           
           }
       }
    }
  }
  delay(10);
} 



Como se observa el código cada vez que vamos a escribir o leer datos activamos o desactivamos respectivamente el pin que va conectado a DE y RE del módulo RS485.

EL maestro siempre tiene activo la línea como transmisión pudiendo escribir en cualquier momento, mientras que el esclavo siempre está en modo recepción, escuchando los datos que le lleguen. Cuando el maestro necesite una respuesta por parte del esclavo, después de enviar la consulta, debe cambiar a modo de receptor para que el esclavo puede usar el canal para transmitir los datos, finalizado la respuesta, el maestro nuevamente debe tener libre el canal para transmitir.


Ej 4. Comunicación half dúplex entre varios arduinos.

Esta es la configuración más común que se usa, todos los Arduinos están conectados al mismo bus RS485, Un Arduino es maestro y todos los demás son Esclavos. Cada esclavo tiene una dirección el cual le identifica, el maestro para que pueda comunicarse con un esclavo usa esta dirección. El maestro envía la información por el bus y solo el esclavo con la dirección correcta es quien interpreta o procesa los datos.

A continuación mostramos el mismo ejemplo que se está trabajando anteriormente, pero enfocado une una conexión multipunto.

Las conexiones serían las siguientes

Uploaded Image: Arduino RS485 half duplex multipunto Maestro Esclavo.jpg
Imagen: Arduino RS485 half duplex multipunto Maestro Esclavo

El ejemplo solo se muestra para un esclavo pero para los otros esclavos las conexiones son las mismas y en el código solo hay que tener en cuenta las direcciones de los esclavos.

La diferencia con los casos anteriores es en la trama para la comunicación, ahora es necesario enviar la dirección del esclavo, quedando la trama de la siguiente forma:

 [Inicio de trama][Dirección][Función][Valor][Fin de trama]

La dirección es un número entero e indica con cual esclavo nos queremos comunicar. Ejemplos de esta trama serian: “I101S90F”,”I25S120F”,”I223LF”, etc.

El esclavo interpreta esta trama y si coincide su dirección entonces realiza la función correspondiente, si es una función que necesite responder, en la trama de respuesta también agrega su dirección, esto para que el maestro sepa que ha respondido el esclavo correspondiente. La trama de respuesta es de la forma:

 [Inicio de trama][Dirección] [, ] [Valor][Fin de trama]

En este caso entre la dirección y el valor enviamos una coma como separador puesto que ambos son valores numéricos, unos ejemplo de trama serian: “i101,865f”, “i26,64f


Código del Maestro:

const int ledPin =  13; // Numero del pin para el Led     
const int EnTxPin =  2; // HIGH:TX y LOW:RX
void setup() 
{ 
  Serial.begin(9600);
  Serial.setTimeout(100); //establecemos un tiempo de espera de 100ms
  // inicializamos los pines
  pinMode(ledPin, OUTPUT);
  pinMode(EnTxPin, OUTPUT);
  digitalWrite(ledPin, LOW); 
  digitalWrite(EnTxPin, HIGH); //RS485 como Transmisor
} 
 
void loop() 
{ 
   
  int lectura = analogRead(0);//leemos el valor del potenciómetro (de 0 a 1023) 
  int angulo= map(lectura, 0, 1023, 0, 180);// escalamos la lectura a un valor de ángulo (entre 0 y 180)
  //---enviamos el ángulo para mover el servo------
  Serial.print("I"); //inicio de trama
  Serial.print("101");//dirección del esclavo
  Serial.print("S"); //función  S para indicarle que vamos a mover el servo
  Serial.print(angulo); //ángulo  o dato
  Serial.print("F"); //fin de trama
  //----------------------------
  delay(50); 
  //---solicitamos una lectura del sensor----------
  Serial.print("I"); //inicio de trama
  Serial.print("101");//direccion del esclavo
  Serial.print("L"); //L para indicarle que vamos a Leer el sensor
  Serial.print("F"); //fin de trama
  Serial.flush();    //Esperamos hasta que se envíen los datos
  //----Leemos la respuesta del Esclavo-----
  digitalWrite(EnTxPin, LOW); //RS485 como receptor
  if(Serial.find("i")) //esperamos el inicio de trama
  {
      int esclavo=Serial.parseInt();  //recibimos la direccion del esclavo
      int dato=Serial.parseInt();  //recibimos el dato
      if(Serial.read()=='f'&&esclavo==101) //si fin de trama y direccion son los correctos
      {
         funcion(dato);   //realizamos la acción con el dato recibido
      }
  }
      digitalWrite(EnTxPin, HIGH); //RS485 como Transmisor
  //----------fin de la respuesta----------
  
} 
void funcion(int dato)
{
  if(dato>500)
  digitalWrite(ledPin, HIGH); 
  else
  digitalWrite(ledPin, LOW); 
}



Código del Esclavo:

#include <Servo.h> 
 
Servo myservo;  // creamos el objeto servo 
const int EnTxPin =  2;  // HIGH:TX y LOW:RX
const int mydireccion =101; //Direccion del esclavo
void setup() 
{ 
  Serial.begin(9600);  
  Serial.setTimeout(100);  //establecemos un tiempo de espera de 100ms
  myservo.attach(9);  // asignamos el pin 9 para el servo.
  pinMode(EnTxPin, OUTPUT);
  digitalWrite(EnTxPin, LOW); //RS485 como receptor
} 
 
void loop() 
{ 
  if(Serial.available())
  {
    if(Serial.read()=='I') //Si recibimos el inicio de trama
    {
        int direccion=Serial.parseInt(); //recibimos la direccion 
        if(direccion==mydireccion) //Si direccion es la nuestra
        {
            char funcion=Serial.read(); //leemos el carácter de función
          
            //---Si el carácter de función es una S entonces la trama es para mover el motor----------- 
            if(funcion=='S') 
             {
                 int angulo=Serial.parseInt(); //recibimos el ángulo
                 if(Serial.read()=='F') //Si el fin de trama es el correcto
                 {
                   if(angulo<=180) //verificamos que sea un valor en el rango del servo
                    {
                      myservo.write(angulo); //movemos el servomotor al ángulo correspondiente.
                    }   
                 }
             }
             //---Si el carácter de función  es L entonces el maestro está solicitando una lectura del sensor---
             else if(funcion=='L')
             {
                if(Serial.read()=='F') //Si el fin de trama es el correcto
                 {
                   int lectura = analogRead(0); //realizamos  la lectura del sensor     
                   digitalWrite(EnTxPin, HIGH); //rs485 como transmisor
                    Serial.print("i"); //inicio de trama  
                   Serial.print(mydireccion); //direccion   
                   Serial.print(",");       
                   Serial.print(lectura); //valor del sensor
                   Serial.print("f"); //fin de trama  
                   Serial.flush(); //Esperamos hasta que se envíen los datos
                   digitalWrite(EnTxPin, LOW); //RS485 como receptor             
                 }
             }
        }
    }
  }
  delay(10);
} 



El código para un segundo esclavo si se desea que tenga la misma función, en el código solo debe cambiar su dirección.


Comentarios:
...
Jo*** 11/07/2017
Excelente aporte, podrian ayudarme mi cuestion es que la comunicacion funciona muy bien pero entre 2 arduinos, como hago para comunicar 5 esclavos con el master , el master va llamando uno por uno y esperando la repuesta de cada uno y ahi si llama al siguiente, pero no me funciona
Agradezco de su ayuda, gracias...

Xa***** 30/01/2018
Hola, elimina la resistencia R7 (la de 120R) de los dispositivos que estén en medio de tu red. Los extremos SÍ DEBEN LLEVARLA.

...
An***** C. 15/01/2017
Saludos.
En primer lugar, enhorabuena y muchas gracias por este post y por los demás. Son claros, bien explicados y fáciles de seguir.
Estoy haciendo un proyecto en el que necesito activar unos reles desde un puesto central, distante unos 8 metros de estos. Comencé la construcción siguiendo tus explicaciones de la "Comunicación Half-Duplex entre Arduinos" y utilizando un Arduino UNO como Maestro y Arduino NANO en cada esclavo. Al conectar todo en el primero de ellos y probar todo fue perfecto. Sin embargo no he conseguido que funcione un segundo. He revisado y sustituido el cableado, he intercambiado las placas NANO, he intercambiado las placas RS485 y siempre he obtenido el mismo y frustrante resultado. El "puesto" que funciona SIEMPRE funciona (con cualquier placa) y el que no funciona NUNCA funciona. He instalado una pantalla que me confirma el comando enviado, incluyendo la dirección y, aunque se produce el pequeño "destello en ambas placas NANO de que algo ha llegado, el resultado siempre es el mismo. Hay algo adicional a comprobar o instalar? Gracias de antemano y disculpa la perorata.

Ad** 22/03/2017
Que tal Antonio C. ya que toda la operación entre arduinos se realiza de forma correcta, me gustaría saber si tienes conectados los relevadores a una fuente externa y activados por transistor, ya que si los tienes directo a la arduino no tiene la corriente necesaria para activar la bobina.

Xa**** 30/01/2018
El tutorial no menciona algo SUMAMENTE IMPORTANTE: Los dispositivos extremos DEBEN incluir la resistencia de 120R (es decir, R7), mientras que los dispositivos de enmedio NO DEBEN incluirla. Además, me atrevería a recomendar QUITAR las resistencias de pull-up (R1..R4).

Esto es lo malo de Arduino, de los pseudo-expertos y de los aprendices que siempre serán aprendices: nadie se toma el tiempo suficiente para ENTENDER lo que se está haciendo. Desgraciadamente todo termina en un copy-and-paste, y a ver si con algo de suerte lo que sea que estemos haciendo funciona.

En este caso en particular, mi recomendación es leer sobre RS485 en libros y sitios en inglés, son los más completos y exactos.
...


...

==CUT===



 https://know.innon.com/bias-termination-rs485-network

Hay que poner terminadores de 120 Ohm en los dispositivos rs485 de los extremos:
Uploaded Image: rs485-termination.png

Hay que poner pull-ups/pull-downs de unos 600 Ohm en el primer dispositivo rs485:
Uploaded Image: rs485-bias.png