desktop

Problemas con comunicación RS485-RS232 (ARDUINO)

torres.electronico

Well-known-Alfil
Buenas, estoy trabajando en un proyecto de comunicación y me tope con varios problemas que me dieron dolores de cabeza... Para poder llegar al índice, raíz del conflicto que me esta haciendo ponchear, tuve que hacer varios parches/modificaciones para deducir y llegar a la conclusión que estoy teniendo problema con el desglose e interpretación de la trama de datos...
El circuito es el siguiente... La idea era hacer un ejemplo de adquisición de datos y comunicación entre dispositivos implementando el conversor RS485-RS232, para luego volcar todos esos datos en una plantilla Excel a modo registrador.

RS485_RS232.png

Los programas implementados hasta el momento funcionan, pero falta el puntero mas importante, que es el que nos indica el fin de transmisión... Probé de diversas maneras (trabajando los datos en formato bytes, HEX, char, etc.) y siempre me pasa lo mismo... Arranco subiendo los programas del MASTER y los 2 ESCLAVOS

MASTER
CSS:
/////////////////////////////////////////////////////////////////////////
// Ejemplo de comunicacion RS485-RS232 con ARDUINO (Sketch del MASTER) //
/////////////////////////////////////////////////////////////////////////

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,20,4);

int value1;
int value2;
int slave2;
int slave1;

const int LED =  13; 
const int Enable =  2;

void setup()
{
  Serial.begin(9600);
  Serial.setTimeout(250);

  pinMode(LED, OUTPUT);
  pinMode(Enable, OUTPUT);
  digitalWrite(LED, LOW);
  digitalWrite(Enable, HIGH); // inicio de transmision al esclavo 1
  lcd.init();   
  lcd.backlight();
  lcd.clear();
}

void loop()
{
        lcd.setCursor(0,0);
        lcd.print("****RS232  RS485****");
        lcd.setCursor(0,3);
        lcd.print("**********ETI*********");

  Serial.println("-");
  delay(10);
  digitalWrite(Enable, HIGH); // inicio de transmision al esclavo 1
  Serial.print("I");
  Serial.print("1");
  Serial.print("L");
  Serial.print("F");
  Serial.flush();
  //delay(10);
  digitalWrite(Enable, LOW); // fin de transmision al esclavo 1
 
 
 
  if(Serial.available())
  {
  if(Serial.find("i"))
  {
    
    int direccion = Serial.parseInt(); //recibimos la direccion
  
      if((direccion)==1) //Si direccion es la nuestra
       {
  
        value1 = Serial.parseInt();
         lcd.setCursor(0,1);
        lcd.print("Esclavo 1:");lcd.print(value1);
      }
 }
  Serial.println("-");
  delay(10);
  digitalWrite(Enable, HIGH); // inicio de transmision al esclavo 2
  Serial.print("I");
  Serial.print("2");
  Serial.print("L");
  Serial.print("F");
  Serial.flush();
  //delay(10);
  digitalWrite(Enable, LOW);  // fin de transmision al esclavo 2

   if(Serial.available())
  {
        //lcd.setCursor(0,0);
        //lcd.print("******DATO**********");
        // con esta parte iba viendo hasta donde reconocía los comandos del esclavo
        //lcd.setCursor(0,3);
        //lcd.print("**********ETI*********");
  if(Serial.find("i"))
  { 
    
      int direccion=Serial.parseInt(); //recibimos la direccion
      if(direccion==2) //Si direccion es la nuestra
       {
        value2 = Serial.parseInt();
      
        lcd.setCursor(0,2);
        lcd.print("Esclavo 2:");lcd.print(value2);
      }
  }
      digitalWrite(Enable, LOW);
}
}
}


ESCLAVO1
CSS:
/////////////////////////////////////////////////////////////////////////
// Ejemplo de comunicacion RS485-RS232 con ARDUINO (Sketch del ESCLAVO1) //
/////////////////////////////////////////////////////////////////////////
const int Enable =  2;
const int SlaveNumber = 1;

void setup()
{
  Serial.begin(9600);
  Serial.setTimeout(250);
  pinMode(Enable, OUTPUT);
  digitalWrite(Enable, LOW);
  pinMode(13, OUTPUT);
  pinMode(A0, INPUT);
}

void loop()
{
  if(Serial.available())
  {
    if(Serial.read()=='I')
    { 
        int Slave = Serial.parseInt();
        if(Slave == SlaveNumber)
        { 
            char command = Serial.read();     
          
             if(command == 'L')
             {
                if(Serial.read()=='F')
                 {
                   int lectura = analogRead(0);
                   int AnalogValue = map(lectura, 0, 1023, 0, 255);               
                      
                   digitalWrite(Enable, HIGH);
                
                   Serial.print("i");
                   Serial.print(" ");
                   Serial.print(SlaveNumber);
                   Serial.print(" "); 
                   Serial.print(AnalogValue);
                   Serial.print(" ");
                   Serial.print("f");
                   Serial.print(" ");
                   Serial.flush();
                
                   digitalWrite(Enable, LOW);         
                 }
             }
          
        }
    }
  }
  delay(10);
}


ESCLAVO2
CSS:
/////////////////////////////////////////////////////////////////////////
// Ejemplo de comunicacion RS485-RS232 con ARDUINO (Sketch del ESCLAVO2) //
/////////////////////////////////////////////////////////////////////////
const int Enable =  2;
const int SlaveNumber = 2;

void setup()
{
  Serial.begin(9600);
  Serial.setTimeout(250);
  pinMode(Enable, OUTPUT);
  digitalWrite(Enable, LOW);
  pinMode(13, OUTPUT);
  pinMode(A0, INPUT);
}

void loop()
{
  if(Serial.available())
  {
    if(Serial.read()=='I')
    { 
        int Slave = Serial.parseInt();
        if(Slave == SlaveNumber)
        { 
            char command = Serial.read();     
          
             if(command == 'L')
             {
                if(Serial.read()=='F')
                 {
                   int lectura = analogRead(0);
                   int AnalogValue = map(lectura, 0, 1023, 0, 255);               
                      
                   digitalWrite(Enable, HIGH);
                
                    Serial.print("i");
                   Serial.print(" ");
                   Serial.print(SlaveNumber);
                   Serial.print(" "); 
                   Serial.print(AnalogValue);
                   Serial.print(" ");
                   Serial.print("f");
                   Serial.print(" ");
                   Serial.flush();
                
                   digitalWrite(Enable, LOW);         
                 }
             }
          
        }
    }
  }
  delay(10);
}

Bien, cuando el esclavo recibe la trama de datos y reconoce los caracteres de comandos, automáticamente responde, pero el MASTER no diferencia la direccion del valor del sensor... ahi arranco parte del problema

CSS:
       if(Serial.read()=='F')
                  {
                  int lectura = analogRead(0);                      //Lectura del puerto analogico
                  int AnalogValue = map(lectura, 0, 1023, 0, 255);  // mapeamos la señal para que nos de un numero de 0 a 255             
                
                  digitalWrite(Enable, HIGH); // Inicio de transmision, queda en modo ENVIO de DATOS
                  Serial.print("i");          // envio el puntero indicador de que el esclavo responde
                  Serial.print(" ");          // Lo implemento para generar un espacio entre datos
                  Serial.print(SlaveNumber);  // Envio el numero de dispositivo ESCLAVO
                  Serial.print(" ");          // Lo implemento para generar un espacio entre datos
                  Serial.print(AnalogValue);  // Envio el valor de la lectura analogica
                  Serial.print(" ");          // Lo implemento para generar un espacio entre datos
                  Serial.print("f");          // envio de comando puntero de fin de trama
                  Serial.print(" ");          // Lo implemento para generar un espacio entre datos
                  Serial.flush();             // Limpia el buffer
                  digitalWrite(Enable, LOW);  // Fin de transmision, queda en modo ESCUCHA         
                  }

Bien, implementando los espacios entre datos, logre que el master interprete la trama, pero el puntero mas importante (f) que me indica que finaliza la trama de datos, no me lo interpreta, asi que lo obvie y el programa corre normalmente... Lei que Serial.parseInt(); lee la trama y solo toma los valores numéricos, descartando los caracteres... En efecto, arme un programa corto para ver como se comportaba, y funciona de esta manera... El tema es que me junta todos los valores numéricos y no es desglosado, lo cual lleva a tener el error de que no interprete la dirección y quede estancado en la primer condicional...
Alguien podría citarme un ejemplo practico (probado físicamente o en proteus) que funcione? Mas que nada para entender como puedo generar punteros para diferenciar los datos y hacer el desglose de este.
Gracias a todos...
 
Aclaremos que F y f no es igual, si quieres usar ambas usa en su lugar if( var == 'F' || var == 'f'), esto dará true en ambos casos. con la I tienes el mismo problema, el master envía I y espera i, el esclavo envía i y espera I, te estas enredando solo por ello.
 
Se sobreentiende (y)Vamos de nuevo. Si lees el sketch del máster y del esclavo, F es el comando enviado por el máster, y f es el comando que manda el esclavo... También probé como decís en las primeras pruebas, y pasa exactamente lo mismo
 
Se sobreentiende (y)Vamos de nuevo. Si lees el sketch del máster y del esclavo, F es el comando enviado por el máster, y f es el comando que manda el esclavo... También probé como decís en las primeras pruebas, y pasa exactamente lo mismo
No se 'sobreentiende' nada, en el código de máster en ningún momento se busca por F o f, eso solo lo pones en un fragmento de código CSS que no dice de donde sale, si asumo que era lo que intentaste implementar en el máster te dará error, publica tu código completo si quieres una mejor opinión, el que publica solo dice que si recibe una i lea un numero y muestre un texto si es 1, luego vuelve a generar otra condicional pero el algoritmo es terrible, en ningún momento procesa el dato, y piensa ¿que pasará si recibe 2 cuando espera 1? Se perderá el dato completamente y lo saltará, deberías leer el dato primero
Int data = Serial.read()
Luego hacer las comparativas
If(data == '1'){}
Else If(data == '2'){}
No hacer lecturas en cada if() por que cada ves que ejecutas read se desplaza el dato y se borra del buffer de entrada a menos de que uses peek()
 
El sketch está completo y ya te dije que probé de esas maneras que mencionas y no funcionó...así como esta funciona... Discúlpame que sea corta tu comprensión, te voy agradecer tu silencio, gracias
 
Última edición:
El sketch está completo y ya te dije que probé de esas maneras que mencionas y no funcionó...así como esta funciona... Discúlpame que sea corta tu comprensión, te voy agradecer tu silencio, gracias

El Master genera una consulta enviando una cadena "-\r\n" y luego espera 10ms para activar la salida enviar "I1LF", espera a que se termine de transmitir, después revisa si hay datos, serial en Arduino usa interrupciones por hardware así que no hay perdida en esa parte pese al retraso, lo que lees es el buffer no el puntero de entrada.
Primero el master compara si existe el dato "i" si existe luego espera un valor decimal int en la siguiente parte de código y si es 1, vuelve a consultar por otro valor int, al recibirlo lo muestra en pantalla.
Después manda la cadena "I2LF" y básicamente repite lo mismo

¿En cuál parte estás buscando por la F o f? si crees que a mí me falta compresión, al Arduino también le faltará adivinar en qué momento debía buscar una f. Por favor comparte el código que te da error para que se te asista mejor, personalmente lo hago comparando un buffer de entrada conociendo el esquema y enviando los datos en binario no con texto formateado para reducir la carga del MCU.

Si se te complica te recomendaría probar usando json ya que es una forma simple de definir paquetes.
Json es un formato en el que pones {"parametro1":valor, "parametro2":"valor de cadena", "parametroArray":[valor1, valor2,valor3]}
un ejemplo simple sería este:
C:
#include <ArduinoJson.h>
JsonDocument packet;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  if (Serial.available()) {
    String dataStream = Serial.readString();
    DeserializationError error = deserializeJson(packet, dataStream);
    if (error) {
      Serial.print(F("Ocurrió un error: "));
      Serial.println(error.f_str());
    } else {
      const int dev = packet["dev"];
      const int data = packet["val"];
      Serial.println("se recibieron los siguientes valores:");
      Serial.print("Dispositivo: ");
      Serial.print(dev);
      Serial.print(" Lectura: ");
      Serial.println(data);
    }
  }
}

Serial.readString() se usa para leer el buffer de entrada hasta que ocurra un timeout que es un tiempo sin recibir datos por lo que asumes la transmisión completada. la String resultante la mandas al deserializador y este se encarga de leer los valores y asignarlos.
Con el código que te pongo por ejemplo envias este texto en la consola {"dev":2,"val":247} y te debería devolver la siguiente respuesta:
se recibieron los siguientes valores:
Dispositivo: 2 Lectura: 247
El intérprete se encargará de manejar los datos, si no cierro la cadena por ejemplo y solo enviamos {"dev":2,"val":2 retornará un error
Ocurrió un error: IncompleteInput
Cada objeto inicia con { y finaliza con }, el nombre del parámetro lo defines entre comillas, pones : para indicar el valor, si es numérico lo pones directo, si es cadena lo pones entre comilla, separas cada parametro:valor con , y puedes usar [] para las matrices si llegaras a necesitarlas.
 
te comparto una captura de los tres sketch compilados tal cual como están arriba presentados, y quería mostrarte que me interpreta el carácter con ´ ´ o con " "...
En el video, vas a poder ver que en un esclavo uso los separadores y en el otro no... e igual me los interpreta el master...
Este ultimo mensaje que escribiste, me sirvió para entender que voy a tener que leer, estudiar y comprender mas algunas funciones, ya que mi fuerte en realidad no son los lenguajes C, si no mas bien ASM y los BASIC.
Por casualidad, tendrás algún ejemplo de comunicación RS485 HALF DUPLEX entre tres o mas dispositivos? Busque ejemplos, y todos me llevan a prácticamente los mismos ejemplos del tutorial donde arranque a experimentar.
 
Veo código complejo y poco optimizado.

-Define cada esclavo con una variable booleana: esclavo 1=0, esclavo 2=1 y luego distinguelos con un simple if:

valor=SerialRead()
if valor==1 then
'esclavo 1
else
'esclavo2

-Abundando en lo mismo, en vez de mandar la cadena "I1LF" o "I2LF" y luego tener que andar buscando la I y el valor a continuación usando ifs anidados, mejor manda mejor "0LF" ó "1LF" y listo.
 
Veo código complejo y poco optimizado.

-Define cada esclavo con una variable booleana: esclavo 1=0, esclavo 2=1 y luego distinguelos con un simple if:

valor=SerialRead()
if valor==1 then
'esclavo 1
else
'esclavo2

-Abundando en lo mismo, en vez de mandar la cadena "I1LF" o "I2LF" y luego tener que andar buscando la I y el valor a continuación usando ifs anidados, mejor manda mejor "0LF" ó "1LF" y listo.
El tema estaría cuando tenga más de dos esclavos (guiño guiño)...
Estoy buscando más ejemplos en San Google. No quiero resolverlo como una comunicación Simplex, quiero ver si puedo llegar a entender cómo trabajar half duplex con el C de Arduino y después armar un ejemplo potable... Sinceramente, si miras en Google, muchos copiaron y pegaron lo mismo
 
Es que no hay mucho mas que hacer. RS-485 es half-duplex por naturaleza, así que uno habla y los demás escuchan. Al que le toca luego contesta y despues todos se callan hasta el nuevo diálogo...
Si Doc, si hago como dice Nirvana, funcionaria. Pero por lo general las tramas de datos son de 32bits, y me gustaría entender / aprender como implementar punteros para leer el dato que quiero... Justo ahora estoy mirando un tutorial en youtube nuevamente sobre array, quizás lo pueda enfocar por ese lado
Te lo dije en serio (y)
Me estás malinterpretando dosme.. no reto a nadie. Solo consulté algo puntual y expliqué que ya probé de esas formas y que lo que busco es entenderlo para poder practicarlo. Es más, me dijeron que era imposible que funcione y tuve que subir un vídeo para mostrar que si funciona, pero puntualmente, no funciona como tendría que ser :facepalm:
 
Pero por lo general las tramas de datos son de 32bits, y me gustaría entender / aprender como implementar punteros para leer el dato que quiero.
Lo primero que debés hacer es definir completamente la estructura de los datos a enviar y recibir: cantidad total de bytes/chars, delimitadores de campos, direcciones de los clientes, etc, etc. Con eso ya bien claro podés definir una máquina de estados que opere según esa definición para recuperar el mensaje completo y obtener los datos en él contenidos.
 
Te recomiendo dar un poco de tiempo después de cambiar a modo de recepción porque leer tan rápido puede causarte problemas, si el esclavo está procesando tu master podría asumir que no hay datos y saltar la secuencia, te recomiendo que pongas por ejemplo esto después de que cambias a modo de lectura.
C++:
  int espera = 1000;
  while(Serial.available() == 0 && espera){
    delay(5);
    --espera;
  }
Eso debería realizar un bucle mientras leyendo cada 5ms en busca de datos, una ves de que se vacía el contador (5ms*1000 = 5s) entonces se sale de la espera, la salida también puede ocurrir si detecta datos por lo que puede continuar con el programa normalmente en cualquiera de los 2 casos. Así evitas perder la respuesta y que por culpa de eso el Master comience a transmitir al otro y luego tener incluso hasta los 3 intentando hablar al mismo tiempo.
 
Te recomiendo dar un poco de tiempo después de cambiar a modo de recepción porque leer tan rápido puede causarte problemas, si el esclavo está procesando tu master podría asumir que no hay datos y saltar la secuencia, te recomiendo que pongas por ejemplo esto después de que cambias a modo de lectura.
C++:
  int espera = 1000;
  while(Serial.available() == 0 && espera){
    delay(5);
    --espera;
  }
Eso debería realizar un bucle mientras leyendo cada 5ms en busca de datos, una ves de que se vacía el contador (5ms*1000 = 5s) entonces se sale de la espera, la salida también puede ocurrir si detecta datos por lo que puede continuar con el programa normalmente en cualquiera de los 2 casos. Así evitas perder la respuesta y que por culpa de eso el Master comience a transmitir al otro y luego tener incluso hasta los 3 intentando hablar al mismo tiempo.
Esta noche me pongo con esto nuevamente y modifico los tiempos y veré de probar otras cosas más...Anoche justamente me senté a leer otro aporte tuyo del año pasado, sobre cómo trabajar string, y veo que estoy lejos de tener la idea bien formada de todo lo referente a comunicación serial con este le guaje C de Arduino...
Voy a buscar donde tengo los dos shield rs485 para de ahora en mas, hacerlo más práctico.
Me sucedió en un par de oportunidades que algunos proyectos en Proteus no funcionaban y en protoboard si; Cómo así también, me sucedió que en Proteus funcionaban y luego en el protoboard no :facepalm:
 
Ayer hablaba de la importancia del código bien estructurado y optimizado.
Fíjate en esta sección del código:
if(Serial.read()=='I')
{
int Slave = Serial.parseInt();
if(Slave == SlaveNumber)
{
char command = Serial.read();

if(command == 'L')
{
if(Serial.read()=='F')
{
¿Por qué están las variables Slave y command definidas dentro del conjunto de IFs anidados?
Siempre que se pueda, las variables se definen fuera.

¿Por qué lees los valores "I" y "F" directamente con un SerialRead() y, en cambio, lees "L" mediante la variable auxiliar command, que es un SerialRead()?
Eso sólo tendría sentido si usaras el contenido de command posteriormente, pero no en este caso, que sólo quieres saber si llegó una "L".

----------

En estas rutinas de lectura y escritura en puertos, el timing es crítico.
Todo ese código confuso, mal estructurado y no optimizado, son retrasos en la ejecución que luego hacen que la cosa funcione en la simulación, pero no en la realidad. Hay lugares del código donde no puede haber retrasos. En cambio, en otros lugares hay que introducirlos a propósito para no perder datos.

El tema estaría cuando tenga más de dos esclavos
Ok.
Elige con qué instrucción quieres que te lo implemente:
For...Next
While...Wend
Do...Loop
Select...Case
If ...Then
Try...Catch
Foreach...
 
Última edición:
Atrás
Arriba