RS485

Hola,

He estado jugando un poco con el bus de comunicación RS485. Un bus muy utilizado en automatizaciones y entornos industriales.

El transceiver utilizado es un MAX485:

El motaje:

Son dos Arduinos + una placa de arduino sin el micro conectado para poder utilizar el chip FTDI y monitorizar las tramas en el bus con el ordenador.
Para monitorizar las tramas, así como para la depuración he usado el terminal RealTerm: http://realterm.sourceforge.net/. Es gratuito y permite enviar cadenas en Hexadecimal.
Sólo como receptor, ya que para poder emitir tendría que actuar sobre el pin de control del transceiver MAX485. En el caso del chip FTDI, dispone de un pin para esta función (TXDEN).
Es muy sencillo realizar un conversor USB <- -> RS485 con dicho chip.

La comunicación es Half Duplex, es decir, se puede enviar/recibir mensajes pero no de forma simultanea.

El método de transmisión se trata que el arduino maestro manda una petición y el esclavo manda la respuesta con el dato que se le ha pedido. Estas tramas son similares a las que se pueden encontrar en productos profesionales.

Las tramas son de 15 bytes:
Byte 1: Byte de start ( 0 hexadecimal ).
Byte 2-3: ASCII de la dirección del arduino.
Byte 4: Byte ENQ, ACK ó NAK (0x05h, 0x06h y 0x15h) .
Byte 5: ASCII del comando petición.
Byte 6 y 7: ASCII del número de función.
Byte 8: Byte signo (Positivo 0x20h y Negativo 2D)
Byte 9-12: ASCII con el dato (0x00h-0xFFFFh)
Byte 13: Byte fin de texto (0x03h)
Byte 14-15: Checksum (suma de byte 2 al byte13)

En el ejemplo, el arduino maestro envía la orden de encendido/apagado del led al arduino esclavo. El interruptor está en el maestro y la actuación (encendido/apagado) en el esclavo.

Según el estado del interruptor (en el montaje un triste cable? je,je,je). El maestro envía la trama de petición de encendio/apagado, al esclavo de dirección 01. La función la hemos llamado D y el número 00.
Ej: 0x00 0x30 0x31 0x05 0x44 0x30 0x30 0x20 0x30 0x30 0x30 0x31 0x03 0x01 0xEE
El esclavo, contesta con un ACK al maestro confirmando el dato recibido.
Ej: 0x00 0x30 0x31 0x06 0x44 0x30 0x30 0x20 0x30 0x30 0x30 0x31 0x03 0x01 0xEF

Se puede observar, que el maestro confirma la orden recibida en el byte 4. (6h= ACK).
Ver códigos ASCII:

Con esta trama se dispone de un byte que indica función + 2 bytes con el número de la función, por lo cual se pueden hacer multitud de combinaciones? Algunos ejemplos pueden ser: A01 realiza una petición de datos de la entrada analógica 1, P01 realiza una configuración de PWM 1 con el dato enviado en los 4 bytes de datos,?

Aqui puedes ver un video demostrativo:

Con el bus RS485 resulta relativamente sencillo y económico crear redes. Sencillamente puedes montar una red domótica en casa...je,je,je

Cabe descatar que al igual que el bus CAN, el bus RS485 tiene que terminarse ambos extremos con una resistencia terminadora de 120 ohmios. Cuando se introducen dispositivos entre medio del bus, deben ir conectados lo más cerca posible del transceptor si se está trabajando a altas velocidades.
Puedes ver en la foto del transceptor como se ha realizado un "empalme" (recomendado menos de 15 cm).

Espero que haya quedao más o menos claro.... :wink:

Salu2

Igor R.

Código del Maestro:

//RS485 by Igor Real

byte            data[12];
unsigned long   previous_time;
byte            times_repeat=5;

byte            state=0;

#define  pinCONTROL    02
#define  myaddress     01
  

void setup() {

   pinMode(13,OUTPUT);
   pinMode(pinCONTROL,OUTPUT);
   digitalWrite(13,HIGH);
   digitalWrite(12,LOW);
   pinMode(8,INPUT);
   digitalWrite(pinCONTROL,LOW);
   Serial.begin(9600);
   Serial.println("Empezamos");
   state=0;
}

void loop() 
{

  delay(500);
  if (digitalRead(8)==state){
    state=!state;
    times_repeat=0;   
  } 
 
  
  
  
  if (times_repeat<4){
    Serial.flush();  
    //(byte address1,byte address2,byte data_type,byte code1,byte code2,byte Sign,byte data1,byte data2,byte data3,byte data4)
    if (digitalRead(8)==HIGH)  sendMSG(48,49,68,48,48,32,48,48,48,49);
    if (digitalRead(8)==LOW)   sendMSG(48,49,68,48,48,32,48,48,48,48);
    times_repeat=times_repeat+1;
  }
  previous_time=millis();
  while (((millis()-previous_time)<500) && (Serial.available()!=15)){
    ;;
  }
  if (Serial.available()>=15){
    if (receiveMSG()==1){
      Serial.println("Trama correcta");
      if (data[0]==48 && data[1]==49 && data[2]==6){
        //ACK  
        times_repeat=5;
        Serial.println("ACK recivido");
      }
    }
   }
}


byte receiveMSG(){

  byte  byte_receive;
  byte  state=0;
  byte  cont1=1;
  byte  trace_OK=0;

  unsigned int checksum;
  unsigned int checksum_trace;
  
  //Serial.println("estoy dentro");
  //Serial.println(Serial.available(),DEC);
  
  
  while (Serial.available() > 0){
     
     byte_receive=Serial.read();
     if (byte_receive==00){
       state=1;
       checksum_trace=0;
       checksum=0;
       trace_OK=0;
       cont1=1;
     }else if (state==1 && cont1<=12){
       data[cont1-1]=byte_receive;
       checksum=checksum+byte_receive;
       cont1=cont1+1;
     }else if (state==1 && cont1==13){
       checksum_trace=byte_receive<<8;
       cont1=cont1+1; 
     }else if (state==1 && cont1==14){
       checksum_trace=checksum_trace+byte_receive;
       cont1=cont1+1;
       state=0;
       if (checksum_trace==checksum){
           trace_OK=1;
       }else{
         trace_OK=0;
       }
       break;
     }
  }
  return trace_OK;

}



void sendMSG(byte address1,byte address2,byte data_type,byte code1,byte code2,byte Sign,byte data1,byte data2,byte data3,byte data4){
  
  unsigned int checksum_ACK;
  checksum_ACK=address1+address2+5+data_type+code1+code2+Sign+data1+data2+data3+data4+3;
  
  digitalWrite(pinCONTROL,HIGH);
  delay(100);
  //Serial.println("Trama ACK");
  //Serial.print("Checksum:");
  //Serial.println(checksum_ACK,HEX);
  Serial.print(0,BYTE); 
  Serial.print(address1,BYTE);
  Serial.print(address2,BYTE);
  Serial.print(5,BYTE);
  Serial.print(data_type,BYTE);
  Serial.print(code1,BYTE);
  Serial.print(code2,BYTE);
  Serial.print(Sign,BYTE);
  Serial.print(data1,BYTE);
  Serial.print(data2,BYTE);
  Serial.print(data3,BYTE);
  Serial.print(data4,BYTE);  
  Serial.print(3,BYTE);
  Serial.print(((checksum_ACK>>8)&255),BYTE);
  Serial.print(((checksum_ACK)& 255),BYTE);
  delay(100);
  digitalWrite(pinCONTROL,LOW);

  
  
}



void sendACK(byte address1,byte address2,byte data_type,byte code1,byte code2,byte Sign,byte data1,byte data2,byte data3,byte data4){
  
  unsigned int checksum_ACK;
  checksum_ACK=address1+address2+6+data_type+code1+code2+Sign+data1+data2+data3+data4+3;
  
  digitalWrite(pinCONTROL,HIGH);
  delay(100);
  //Serial.println("Trama ACK");
  //Serial.print("Checksum:");
  //Serial.println(checksum_ACK,HEX);
  Serial.print(0,BYTE); 
  Serial.print(address1,BYTE);
  Serial.print(address2,BYTE);
  Serial.print(6,BYTE);
  Serial.print(data_type,BYTE);
  Serial.print(code1,BYTE);
  Serial.print(code2,BYTE);
  Serial.print(Sign,BYTE);
  Serial.print(data1,BYTE);
  Serial.print(data2,BYTE);
  Serial.print(data3,BYTE);
  Serial.print(data4,BYTE);  
  Serial.print(3,BYTE);
  Serial.print(((checksum_ACK>>8)&255),BYTE);
  Serial.print(((checksum_ACK)&255),BYTE);
  digitalWrite(pinCONTROL,LOW);
  delay(100);
  
  
}

void sendNAK(byte address1,byte address2,byte data_type,byte code1,byte code2,byte Sign,byte data1,byte data2,byte data3,byte data4){
  
  unsigned int checksum_ACK;
  checksum_ACK=address1+address2+6+data_type+code1+code2+Sign+data1+data2+data3+data4+3;
  
  digitalWrite(pinCONTROL,HIGH);
  delay(100);
  //Serial.println("Trama ACK");
  //Serial.print("Checksum:");
  //Serial.println(checksum_ACK,HEX);
  Serial.print(0,BYTE); 
  Serial.print(address1,BYTE);
  Serial.print(address2,BYTE);
  Serial.print(15,BYTE);
  Serial.print(data_type,BYTE);
  Serial.print(code1,BYTE);
  Serial.print(code2,BYTE);
  Serial.print(Sign,BYTE);
  Serial.print(data1,BYTE);
  Serial.print(data2,BYTE);
  Serial.print(data3,BYTE);
  Serial.print(data4,BYTE);  
  Serial.print(3,BYTE);
  Serial.print(((checksum_ACK>>8)&255),BYTE);
  Serial.print(((checksum_ACK)&255),BYTE);
  digitalWrite(pinCONTROL,LOW);
  delay(100);
  
  
}



byte hex2num(byte x){

  byte result;
  
  if (x>=48 && x<=57){
    result=x-48;  
  }else if (x>=65 && x<=70){
    switch(x){
      case 65:
        result=10;
        break;
      case 66:
        result=11;
        break;
      case 67:
        result=12;
        break;
      case 68:
        result=13;
        break;
      case 69:
        result=14;
        break;
      case 70:
        result=15;
        break;     
    }   
  }
  return result;  
}

Código del esclavo:

//RS485 by Igor Real

byte  data[12];
byte  address;
byte  function;
byte  function_code;
unsigned int data_received;
byte  byte_receive;
byte  state=0;
byte  cont1=1;
byte  trace_OK=0;
unsigned int checksum;
unsigned int checksum_trace;

#define  pinCONTROL    02
#define  myaddress     01
  

void setup() {

   pinMode(13,OUTPUT);
   pinMode(pinCONTROL,OUTPUT);
   digitalWrite(2,LOW);
   Serial.begin(9600);
   Serial.println("Empezamos");

}

void loop() 
{

   while (Serial.available() > 0){
     
     byte_receive=Serial.read();
     if (byte_receive==00){
       //Serial.println("Se ha recibido byte Start");
       state=1;
       checksum_trace=0;
       checksum=0;
       trace_OK=0;
       address=0;
       data_received=0;
       cont1=1;
     }else if (state==1 && cont1<=12){
       data[cont1-1]=byte_receive;
       checksum=checksum+byte_receive;
       cont1=cont1+1;
     }else if (state==1 && cont1==13){
       checksum_trace=byte_receive<<8;
       cont1=cont1+1; 
       //Serial.print("Primer Byte Checksum");      
       //Serial.print(checksum_trace,HEX);
     }else if (state==1 && cont1==14){
       checksum_trace=checksum_trace+byte_receive;
       cont1=cont1+1;
       state=0;
       //Serial.println(byte_receive,HEX);
       //Serial.println("Recibida trama");
       //Serial.print("Checksum Trace= ");
       //Serial.println(checksum_trace,HEX);
       //Serial.print("Checksum= ");
       //Serial.println(checksum,HEX); 
       //Serial.println(checksum,DEC); 
       //Serial.println("Trama= ");
       //Serial.print(data[0]);
       //Serial.print(data[1]);
       //Serial.print(data[2]);
       //Serial.print(data[3]);
       //Serial.print(data[4]);
       //Serial.print(data[5]);
       //Serial.print(data[6]);
       //Serial.print(data[7]);
       //Serial.print(data[8]);
       //Serial.print(data[9]);
       //Serial.print(data[10]);
       //Serial.println(data[11]);       

       if (checksum_trace==checksum){
         trace_OK=1;
         
         address=(hex2num(data[0])<<4)+(hex2num(data[1]));
         function=data[3];
         function_code=(hex2num(data[4])<<4)+(hex2num(data[5]));
         data_received=(hex2num(data[7])<<12)+(hex2num(data[8])<<8)+(hex2num(data[9])<<4)+(hex2num(data[10]));
         
         //Serial.println("TRAZA CORRECTA");
         //Serial.println(address,DEC);
         //Serial.println(data_received);
         if (address==myaddress){
           if ((function=='D') && (function_code==0) && data[2]==5){ 
             if (data_received==1){
               digitalWrite(13,HIGH);
               //Serial.println(data_received,DEC);
               sendACK(data[0],data[1],data[3],data[4],data[5],data[6],data[7],data[8],data[9],data[10]);
             }else if (data_received==0){
               digitalWrite(13,LOW);
               sendACK(data[0],data[1],data[3],data[4],data[5],data[6],data[7],data[8],data[9],data[10]);
             }
           }
         }
       }else{
         //Serial.println("TRAZA INCORRECTA");  
         sendNAK(data[0],data[1],data[3],data[4],data[5],data[6],data[7],data[8],data[9],data[10]);
       }
     }

  } 
  
}


void sendACK(byte address1,byte address2,byte data_type,byte code1,byte code2,byte Sign,byte data1,byte data2,byte data3,byte data4){
  
  unsigned int checksum_ACK;
  checksum_ACK=address1+address2+6+data_type+code1+code2+Sign+data1+data2+data3+data4+3;
  
  digitalWrite(pinCONTROL,HIGH);
  delay(100);
  //Serial.println("Trama ACK");
  //Serial.print("Checksum:");
  //Serial.println(checksum_ACK,HEX);
  Serial.print(0,BYTE); 
  Serial.print(address1,BYTE);
  Serial.print(address2,BYTE);
  Serial.print(6,BYTE);
  Serial.print(data_type,BYTE);
  Serial.print(code1,BYTE);
  Serial.print(code2,BYTE);
  Serial.print(Sign,BYTE);
  Serial.print(data1,BYTE);
  Serial.print(data2,BYTE);
  Serial.print(data3,BYTE);
  Serial.print(data4,BYTE);  
  Serial.print(3,BYTE);
  Serial.print(((checksum_ACK>>8)&255),BYTE);
  Serial.print(((checksum_ACK)&255),BYTE);
  delay(100);
  digitalWrite(pinCONTROL,LOW);

  
  
}

void sendNAK(byte address1,byte address2,byte data_type,byte code1,byte code2,byte Sign,byte data1,byte data2,byte data3,byte data4){
  
  unsigned int checksum_ACK;
  checksum_ACK=address1+address2+6+data_type+code1+code2+Sign+data1+data2+data3+data4+3;
  
  digitalWrite(pinCONTROL,HIGH);
  delay(100);
  //Serial.println("Trama ACK");
  //Serial.print("Checksum:");
  //Serial.println(checksum_ACK,HEX);
  Serial.print(0,BYTE); 
  Serial.print(address1,BYTE);
  Serial.print(address2,BYTE);
  Serial.print(15,BYTE);
  Serial.print(data_type,BYTE);
  Serial.print(code1,BYTE);
  Serial.print(code2,BYTE);
  Serial.print(Sign,BYTE);
  Serial.print(data1,BYTE);
  Serial.print(data2,BYTE);
  Serial.print(data3,BYTE);
  Serial.print(data4,BYTE);  
  Serial.print(3,BYTE);
  Serial.print(((checksum_ACK>>8)&255),BYTE);
  Serial.print(((checksum_ACK)&255),BYTE);
  digitalWrite(pinCONTROL,LOW);
  delay(100);
  
  
}



byte hex2num(byte x){

  byte result;
  
  if (x>=48 && x<=57){
    result=x-48;  
  }else if (x>=65 && x<=70){
    switch(x){
      case 65:
        result=10;
        break;
      case 66:
        result=11;
        break;
      case 67:
        result=12;
        break;
      case 68:
        result=13;
        break;
      case 69:
        result=14;
        break;
      case 70:
        result=15;
        break;     
    }   
  }
  return result;  
}

No he borrado los puntos de depuración que he usado (Serial.print comentados en los diferentes puntos del programa)

Funciones:

byte receiveMSG() --> Guarda en data[] los bytes de la trama y devuelve 0 si el checksum es incorrecto ó viceversa.

void sendMSG(...) --> envia la trama con los datos pasados a la función. He dejado un delay de 100 ms del pin de control (habilitación de transmisión ó recepción). No tengo ahora osciloscopio para ver un valor correcto... asi se asegura tiempo suficiente para que el cambio sea despues que se haya enviado todo el buffer.

void sendACK() --> envia trama de ACK con datos pasados.

void sendNAK --> envia trama de fallo NAK

byte hex2num --> función para convertir ASCII hex en número decimal.

Si no se recibe la trama de confirmación por parte del esclavo, se intenta mandar la trama de petición de datos 4 veces.

:wink:

Un link interesante que explican el RS485:
http://www.neoteo.com/rs485-domotica-al-alcance-de-tu-mano-15810.neo

:wink:

Dejo un diagrama para explicar un poco mejor la trama:

Saludos

Igor R.

Para el control del pin de control Tx/Rx, he modificado la función de envio de trama.

void sendMSG(byte address1,byte address2,byte data_type,byte code1,byte code2,byte Sign,byte data1,byte data2,byte data3,byte data4){
  
  unsigned int checksum_ACK;
  checksum_ACK=address1+address2+5+data_type+code1+code2+Sign+data1+data2+data3+data4+3;
  
  UCSR0A=UCSR0A |(1 << TXC0);
  
  digitalWrite(pinCONTROL,HIGH);
  delay(1);
  Serial.print(0,BYTE); 
  Serial.print(address1,BYTE);
  Serial.print(address2,BYTE);
  Serial.print(5,BYTE);
  Serial.print(data_type,BYTE);
  Serial.print(code1,BYTE);
  Serial.print(code2,BYTE);
  Serial.print(Sign,BYTE);
  Serial.print(data1,BYTE);
  Serial.print(data2,BYTE);
  Serial.print(data3,BYTE);
  Serial.print(data4,BYTE);  
  Serial.print(3,BYTE);
  Serial.print(((checksum_ACK>>8)&255),BYTE);
  Serial.print(((checksum_ACK)& 255),BYTE);
  while (!(UCSR0A & (1 << TXC0)));
  digitalWrite(pinCONTROL,LOW);

  
  
}

Espero hasta que el buffer de envio esta vacio(registro UCSR0A,bit TXC0) para volver el pin de control a estado bajo. He mirado con el osciloscopio y tiene buena pinta.

Salu2

Igor R.

Por cierto, es mucho más barato usar el integrado SN75176, que el MAX485.

Según Amidata:

  • SN75176 => 0'48 euros
  • MAX485 => 2'39 euros

Salu2

Igor R.

Hola Igor,

Tengo algunas dudas:

En el codigo del maestro tengo " #define myaddress 01 " y despues haces " if (digitalRead(8)==HIGH) sendMSG(48,49,68,48,48,32,48,48,48,49);
" esto no haria con que el propio maestro reciba el mensaje?

Cuando un esclavo envia el ACK al maestro, el maestro envia tambien al esclavo otro ACK? Si no envia nada, porque tenemos " void sendACK " en el codigo del maestro?

Para que sirve " void sendNAK y byte hex2num " en el codigo maestro?

Saludos,

Hola,

La entrada 8 es el interruptor, si esta a "1" manda una orden de encendido ó "0" de apagado al esclavo.
Para saber que se ha recibido correctamente la orden, el esclavo correspondiente al cual has enviado la orden, manda la confirmacion ACK y en caso que la trama no se la esperada, por ejemplo, el checksum no cuadra con los datos, envia un NAK.

Estan puestas todas las funciones en ambos Arduinos aunque no se usen.... Porque fue el primer ejemplo y continuaré haciendo cosas...
Lo suyo es meterlo todo en una librería, pero todavía no esta todo al 100%.

Por ejemplo, me acabo de dar cuenta que envio el checksum tal cual, sin convertirlo a ASCII. Lo suyo es enviarlo en ASCII para que no haya problemas... Ya que si el checksum por ejemplo vale 0xFF00h (si lo envias tal cual sin convertirlo a ASCII) el programa pensará que con el 0x00h es un byte de Start de la trama....

La idea es que el Maestro manda un mensaje (trama ENQ=enquire) y el esclavo contesta con un ACK+Dato ó NAK (acknowledge ó no acknowledge).
Por ejemplo, pongamos que implementamos una funcion que sea:

  • Función= "A"
  • Número de función="01"
  • Datos="0000"
    Cuando el mensaje recibe dicha función, devuelve el dato del conversor analógico(entrada 1) . Contestará con un ACK y con el dato de la conversión.

Es como si estuvieras hablando por radio, que también es un medio que normalmente es semi-duplex. Imaginemos que hay 6 interlocutores. Cada uno tiene un distintivo. Y cuando te diriges a uno de ellos por su distintivo, te confirma con un "Recibido" más la info que le pidas. Imagina una compañia de taxis cuando la central les envia peticiones de ir a buscar a clientes.

El RS485 sólo define el nivel físico, por lo cual hay que implementar el "lenguaje". Yo he usado una manera que se puede encontrar en algunos aparatos industriales comerciales. (En concreto, yo he sacado la idea de un proyecto que hice hace mucho tiempo de comunicación con un inversor industrial de la marca Fuji).
Sigue este link:http://www.cdautomation.com/download/ENG_L_M_FUJI_RS485_COMM_for_FRENIC-Mini.PDF

Igor R.

Hola Igor,

Perfectamente entendido y el manual RS485 es muy bueno.

El proyeto que estoy desarrollando es mucho mas sencillo, apenas necesito de la RS485 como medio de llevar la informacion hasta los esclavos (+/- 10 metros), ningún esclavo me envia datos (a no ser el ACK o el NAK).

Lo que necesito es : inicio, address, data y checksum, y si el checksum esta correcto cuando llega al esclavo, el esclavo retorna el ACK o el NAK caso contrario.

La idea da la radio es realmente apropiada, la diferencia es que yo envio un mensaje a todos los interlocutores, y solo el interlocutor XX me contesta que recibió la orden y la ejecuta.

Convertir el checksum en ASCII es perfecto y ya estoy trabajando en ello.

Saludos,

Cristal

La idea es hacer algo genérico. Ya que para muchas aplicaciones con una trama sencilla te vale, pero casi te lleva el mismo tiempo hacerlo bien y ya se queda hecho para siempre....

Por eso he partido de algo profesional, que seguramente supere las opciones de cualquier miniproyecto que voy hacer con Arduino. Y una vez preparada unas tramas genéricas, es rápido hacer aplicaciones específicas.

La cosa es tener todos los datos en ASCII excepto los bytes específicos de control. Porque asi es más fácil de programar. Ya sabes que si llega un 0x00h es el empiece de la trama, porque si fuera un "0" sería el Ascii correspondiente (0x30h). En éste primer ejemplo ha sido un fallo no ponerlo...bueno, poco a poco. :wink:

Igor R.

Hummm.... acabo de encontrar un programa bastante chulo gratuito.
Se llama FREE SERIAL TERMINAL MONITOR (FREE Serial Port Monitor Analyze Your Serial Port Communication Data)

No conocía ninguno que hiciera de "espia" del puerto serie gratuito. Este te deja hacerlo con la versión que bajas de la web.

:wink:

yo usaba el 232analyzer http://www.232analyzer.com/232default.htm

en la versión gratuita esta limitada la opción hexadecimal, pero para usar de monitor, hasta hora me servía aunque leyera en decimal o ASCII, si fuera necesario se traduce y ya ta.

Gracias por compartir el Free Serial Igor. Gracias tambien Jonino.

Igor,

Solo para estar totalmente seguro de la conexión del hardware:

Arduino ------------- Max485
RXD(PD0) -------------- R0
TXD(PD1) -------------- DI
INT0(PD2) ------------- RE+DE

Correcto?

Esta correcto :wink:

Hola,

He ampliado el ejemplo, poniendo 3 arduinos (2 diecimila y un mega).
Uno de ellos actua como maestro, mandando orden de encendido/apagado de led conectado al pin 13 de los esclavos. Dicha orden se efectua por medio de 2 pulsadores conectados al maestro (uno por cada esclavo).

Aqui podeis ver una foto del montaje:


Y el video del funcionamiento:

Saludos :wink:

Igor R.

CODIGO ARDUINO MAESTRO:

//----------------------------------
//RS 485
//By Igor Real
//24-06-09
//----------------------------------


byte            data[12];
unsigned long   previous_time;
unsigned long   previous_time2;
byte            times_repeat=5;
byte            times_repeat2=5;

byte            state=0;
byte            state2=0;

#define  pinCONTROL    02
#define  myaddress     01
  

void setup() {

   pinMode(13,OUTPUT);
   pinMode(pinCONTROL,OUTPUT);
   digitalWrite(13,HIGH);
   digitalWrite(12,LOW);
   pinMode(8,INPUT);
   pinMode(9,INPUT);
   digitalWrite(pinCONTROL,LOW);
   Serial.begin(9600);
   Serial.println("Empezamos");
   state=0;
   state2=0;
}

void loop() 
{

  if (digitalRead(8)==state){
    state=!state;
    times_repeat=0;   
  } 
  if (digitalRead(9)==state2){
    state2=!state2;
    times_repeat2=0;   
  } 
 
  
  
  
  if (times_repeat<4){
    Serial.flush();  
    //(byte address1,byte address2,byte data_type,byte code1,byte code2,byte Sign,byte data1,byte data2,byte data3,byte data4)
    if (digitalRead(8)==HIGH){  
      sendMSG(48,49,68,48,48,32,48,48,48,49);
    }else { 
      sendMSG(48,49,68,48,48,32,48,48,48,48);
    }
    times_repeat=times_repeat+1;
  
    previous_time=millis();
    while (((millis()-previous_time)<500) && (Serial.available()!=15)){
      ;;
    }
  
    if (Serial.available()>=15){
      if (receiveMSG()==1){
        Serial.println("Trama correcta");
        if (data[0]==48 && data[1]==49 && data[2]==6){
          //ACK  
          times_repeat=5;
          Serial.println("ACK recibido");
        }
      }
     }
   }  
   
   

  if (times_repeat2<4){
    Serial.flush();  
    //(byte address1,byte address2,byte data_type,byte code1,byte code2,byte Sign,byte data1,byte data2,byte data3,byte data4)
    if (digitalRead(9)==HIGH){  
      sendMSG(48,50,68,48,48,32,48,48,48,49);
    }else { 
      sendMSG(48,50,68,48,48,32,48,48,48,48);
    }
    times_repeat2=times_repeat2+1;
  
    previous_time2=millis();
    while (((millis()-previous_time2)<500) && (Serial.available()!=15)){
    ;;
    }
  
    if (Serial.available()>=15){
      if (receiveMSG()==1){
        //Serial.println("Trama correcta");
        if (data[0]==48 && data[1]==50 && data[2]==6){
          //ACK  
          times_repeat2=5;
          //Serial.println("ACK recibido");
        }
      }
    }
  }
   
   
   
}


//------------------------
//FUNCIONES
//------------------------

byte receiveMSG(){

  byte  byte_receive;
  byte  state=0;
  byte  cont1=1;
  byte  trace_OK=0;

  unsigned int checksum;
  unsigned int checksum_trace;
  
  
  
  while (Serial.available() > 0){
     
     byte_receive=Serial.read();
     if (byte_receive==00){
       state=1;
       checksum_trace=0;
       checksum=0;
       trace_OK=0;
       cont1=1;
     }else if (state==1 && cont1<=12){
       data[cont1-1]=byte_receive;
       checksum=checksum+byte_receive;
       cont1=cont1+1;
     }else if (state==1 && cont1==13){
       checksum_trace=byte_receive<<8;
       cont1=cont1+1; 
     }else if (state==1 && cont1==14){
       checksum_trace=checksum_trace+byte_receive;
       cont1=cont1+1;
       state=0;
       if (checksum_trace==checksum){
           trace_OK=1;
       }else{
         trace_OK=0;
       }
       break;
     }
  }
  return trace_OK;

}



void sendMSG(byte address1,byte address2,byte data_type,byte code1,byte code2,byte Sign,byte data1,byte data2,byte data3,byte data4){
  
  unsigned int checksum_ACK;
  checksum_ACK=address1+address2+5+data_type+code1+code2+Sign+data1+data2+data3+data4+3;
  
  UCSR0A=UCSR0A |(1 << TXC0);
  
  digitalWrite(pinCONTROL,HIGH);
  delay(1);

  Serial.print(0,BYTE); 
  Serial.print(address1,BYTE);
  Serial.print(address2,BYTE);
  Serial.print(5,BYTE);
  Serial.print(data_type,BYTE);
  Serial.print(code1,BYTE);
  Serial.print(code2,BYTE);
  Serial.print(Sign,BYTE);
  Serial.print(data1,BYTE);
  Serial.print(data2,BYTE);
  Serial.print(data3,BYTE);
  Serial.print(data4,BYTE);  
  Serial.print(3,BYTE);
  Serial.print(((checksum_ACK>>8)&255),BYTE);
  Serial.print(((checksum_ACK)& 255),BYTE);
  while (!(UCSR0A & (1 << TXC0)));
  digitalWrite(pinCONTROL,LOW);

  
  
}



void sendACK(byte address1,byte address2,byte data_type,byte code1,byte code2,byte Sign,byte data1,byte data2,byte data3,byte data4){
  
  unsigned int checksum_ACK;
  checksum_ACK=address1+address2+6+data_type+code1+code2+Sign+data1+data2+data3+data4+3;
  
  UCSR0A=UCSR0A |(1 << TXC0);
  
  digitalWrite(pinCONTROL,HIGH);
  delay(1);

  Serial.print(0,BYTE); 
  Serial.print(address1,BYTE);
  Serial.print(address2,BYTE);
  Serial.print(6,BYTE);
  Serial.print(data_type,BYTE);
  Serial.print(code1,BYTE);
  Serial.print(code2,BYTE);
  Serial.print(Sign,BYTE);
  Serial.print(data1,BYTE);
  Serial.print(data2,BYTE);
  Serial.print(data3,BYTE);
  Serial.print(data4,BYTE);  
  Serial.print(3,BYTE);
  Serial.print(((checksum_ACK>>8)&255),BYTE);
  Serial.print(((checksum_ACK)&255),BYTE);
  while (!(UCSR0A & (1 << TXC0)));
  digitalWrite(pinCONTROL,LOW);
  
  
}

void sendNAK(byte address1,byte address2,byte data_type,byte code1,byte code2,byte Sign,byte data1,byte data2,byte data3,byte data4){
  
  unsigned int checksum_ACK;
  checksum_ACK=address1+address2+6+data_type+code1+code2+Sign+data1+data2+data3+data4+3;
  
  UCSR0A=UCSR0A |(1 << TXC0);
  
  digitalWrite(pinCONTROL,HIGH);
  delay(1);

  Serial.print(0,BYTE); 
  Serial.print(address1,BYTE);
  Serial.print(address2,BYTE);
  Serial.print(15,BYTE);
  Serial.print(data_type,BYTE);
  Serial.print(code1,BYTE);
  Serial.print(code2,BYTE);
  Serial.print(Sign,BYTE);
  Serial.print(data1,BYTE);
  Serial.print(data2,BYTE);
  Serial.print(data3,BYTE);
  Serial.print(data4,BYTE);  
  Serial.print(3,BYTE);
  Serial.print(((checksum_ACK>>8)&255),BYTE);
  Serial.print(((checksum_ACK)&255),BYTE);
  while (!(UCSR0A & (1 << TXC0)));
  digitalWrite(pinCONTROL,LOW);
  
  
}



byte hex2num(byte x){

  byte result;
  
  if (x>=48 && x<=57){
    result=x-48;  
  }else if (x>=65 && x<=70){
    switch(x){
      case 65:
        result=10;
        break;
      case 66:
        result=11;
        break;
      case 67:
        result=12;
        break;
      case 68:
        result=13;
        break;
      case 69:
        result=14;
        break;
      case 70:
        result=15;
        break;     
    }   
  }
  return result;  
}

CÓDIGO ARDUINOS ESCLAVOS:
(hay que cambiar la dirección en myaddress a 01 y 02,dependiendo del esclavo)

//----------------------------------
//RS 485
//By Igor Real
//24-06-09
//----------------------------------


byte  data[12];
byte  address;
byte  function;
byte  function_code;
unsigned int data_received;
byte  byte_receive;
byte  state=0;
byte  cont1=1;
byte  trace_OK=0;
unsigned int checksum;
unsigned int checksum_trace;

#define  pinCONTROL    02
#define  myaddress     02
  

void setup() {

   pinMode(13,OUTPUT);
   pinMode(pinCONTROL,OUTPUT);
   digitalWrite(2,LOW);
   Serial.begin(9600);
   Serial.println("Empezamos");

}

void loop() 
{

   while (Serial.available() > 0){
     
     byte_receive=Serial.read();
     if (byte_receive==00){
       //Serial.println("Se ha recibido byte Start");
       state=1;
       checksum_trace=0;
       checksum=0;
       trace_OK=0;
       address=0;
       data_received=0;
       cont1=1;
     }else if (state==1 && cont1<=12){
       data[cont1-1]=byte_receive;
       checksum=checksum+byte_receive;
       cont1=cont1+1;
     }else if (state==1 && cont1==13){
       checksum_trace=byte_receive<<8;
       cont1=cont1+1; 
       //Serial.print("Primer Byte Checksum");      
       //Serial.print(checksum_trace,HEX);
     }else if (state==1 && cont1==14){
       checksum_trace=checksum_trace+byte_receive;
       cont1=cont1+1;
       state=0;
       //Serial.println(byte_receive,HEX);
       //Serial.println("Recibida trama");
       //Serial.print("Checksum Trace= ");
       //Serial.println(checksum_trace,HEX);
       //Serial.print("Checksum= ");
       //Serial.println(checksum,HEX); 
       //Serial.println(checksum,DEC); 
       //Serial.println("Trama= ");
       //Serial.print(data[0]);
       //Serial.print(data[1]);
       //Serial.print(data[2]);
       //Serial.print(data[3]);
       //Serial.print(data[4]);
       //Serial.print(data[5]);
       //Serial.print(data[6]);
       //Serial.print(data[7]);
       //Serial.print(data[8]);
       //Serial.print(data[9]);
       //Serial.print(data[10]);
       //Serial.println(data[11]);       

       if (checksum_trace==checksum){
         trace_OK=1;
         
         address=(hex2num(data[0])<<4)+(hex2num(data[1]));
         function=data[3];
         function_code=(hex2num(data[4])<<4)+(hex2num(data[5]));
         data_received=(hex2num(data[7])<<12)+(hex2num(data[8])<<8)+(hex2num(data[9])<<4)+(hex2num(data[10]));
         
         //Serial.println("TRAZA CORRECTA");
         //Serial.println(address,DEC);
         //Serial.println(data_received);
         if (address==myaddress){
           if ((function=='D') && (function_code==0) && data[2]==5){ 
             if (data_received==1){
               digitalWrite(13,HIGH);
               //Serial.println(data_received,DEC);
               sendACK(data[0],data[1],data[3],data[4],data[5],data[6],data[7],data[8],data[9],data[10]);
             }else if (data_received==0){
               digitalWrite(13,LOW);
               sendACK(data[0],data[1],data[3],data[4],data[5],data[6],data[7],data[8],data[9],data[10]);
             }
           }
         }
       }else{
         //Serial.println("TRAZA INCORRECTA");  
         sendNAK(data[0],data[1],data[3],data[4],data[5],data[6],data[7],data[8],data[9],data[10]);
       }
     }

  } 
  
}


//------------------------
//FUNCIONES
//------------------------

byte receiveMSG(){

  byte  byte_receive;
  byte  state=0;
  byte  cont1=1;
  byte  trace_OK=0;

  unsigned int checksum;
  unsigned int checksum_trace;
  

  
  
  while (Serial.available() > 0){
     
     byte_receive=Serial.read();
     if (byte_receive==00){
       state=1;
       checksum_trace=0;
       checksum=0;
       trace_OK=0;
       cont1=1;
     }else if (state==1 && cont1<=12){
       data[cont1-1]=byte_receive;
       checksum=checksum+byte_receive;
       cont1=cont1+1;
     }else if (state==1 && cont1==13){
       checksum_trace=byte_receive<<8;
       cont1=cont1+1; 
     }else if (state==1 && cont1==14){
       checksum_trace=checksum_trace+byte_receive;
       cont1=cont1+1;
       state=0;
       if (checksum_trace==checksum){
           trace_OK=1;
       }else{
         trace_OK=0;
       }
       break;
     }
  }
  return trace_OK;

}



void sendMSG(byte address1,byte address2,byte data_type,byte code1,byte code2,byte Sign,byte data1,byte data2,byte data3,byte data4){
  
  unsigned int checksum_ACK;
  checksum_ACK=address1+address2+5+data_type+code1+code2+Sign+data1+data2+data3+data4+3;
  
  UCSR0A=UCSR0A |(1 << TXC0);
  
  digitalWrite(pinCONTROL,HIGH);
  delay(1);

  Serial.print(0,BYTE); 
  Serial.print(address1,BYTE);
  Serial.print(address2,BYTE);
  Serial.print(5,BYTE);
  Serial.print(data_type,BYTE);
  Serial.print(code1,BYTE);
  Serial.print(code2,BYTE);
  Serial.print(Sign,BYTE);
  Serial.print(data1,BYTE);
  Serial.print(data2,BYTE);
  Serial.print(data3,BYTE);
  Serial.print(data4,BYTE);  
  Serial.print(3,BYTE);
  Serial.print(((checksum_ACK>>8)&255),BYTE);
  Serial.print(((checksum_ACK)& 255),BYTE);
  while (!(UCSR0A & (1 << TXC0)));
  digitalWrite(pinCONTROL,LOW);

  
  
}



void sendACK(byte address1,byte address2,byte data_type,byte code1,byte code2,byte Sign,byte data1,byte data2,byte data3,byte data4){
  
  unsigned int checksum_ACK;
  checksum_ACK=address1+address2+6+data_type+code1+code2+Sign+data1+data2+data3+data4+3;
  
  UCSR0A=UCSR0A |(1 << TXC0);
  
  digitalWrite(pinCONTROL,HIGH);
  delay(1);

  Serial.print(0,BYTE); 
  Serial.print(address1,BYTE);
  Serial.print(address2,BYTE);
  Serial.print(6,BYTE);
  Serial.print(data_type,BYTE);
  Serial.print(code1,BYTE);
  Serial.print(code2,BYTE);
  Serial.print(Sign,BYTE);
  Serial.print(data1,BYTE);
  Serial.print(data2,BYTE);
  Serial.print(data3,BYTE);
  Serial.print(data4,BYTE);  
  Serial.print(3,BYTE);
  Serial.print(((checksum_ACK>>8)&255),BYTE);
  Serial.print(((checksum_ACK)&255),BYTE);
  while (!(UCSR0A & (1 << TXC0)));
  digitalWrite(pinCONTROL,LOW);
  
  
}

void sendNAK(byte address1,byte address2,byte data_type,byte code1,byte code2,byte Sign,byte data1,byte data2,byte data3,byte data4){
  
  unsigned int checksum_ACK;
  checksum_ACK=address1+address2+6+data_type+code1+code2+Sign+data1+data2+data3+data4+3;
  
  UCSR0A=UCSR0A |(1 << TXC0);
  
  digitalWrite(pinCONTROL,HIGH);
  delay(1);

  Serial.print(0,BYTE); 
  Serial.print(address1,BYTE);
  Serial.print(address2,BYTE);
  Serial.print(15,BYTE);
  Serial.print(data_type,BYTE);
  Serial.print(code1,BYTE);
  Serial.print(code2,BYTE);
  Serial.print(Sign,BYTE);
  Serial.print(data1,BYTE);
  Serial.print(data2,BYTE);
  Serial.print(data3,BYTE);
  Serial.print(data4,BYTE);  
  Serial.print(3,BYTE);
  Serial.print(((checksum_ACK>>8)&255),BYTE);
  Serial.print(((checksum_ACK)&255),BYTE);
  while (!(UCSR0A & (1 << TXC0)));
  digitalWrite(pinCONTROL,LOW);
  
  
}



byte hex2num(byte x){

  byte result;
  
  if (x>=48 && x<=57){
    result=x-48;  
  }else if (x>=65 && x<=70){
    switch(x){
      case 65:
        result=10;
        break;
      case 66:
        result=11;
        break;
      case 67:
        result=12;
        break;
      case 68:
        result=13;
        break;
      case 69:
        result=14;
        break;
      case 70:
        result=15;
        break;     
    }   
  }
  return result;  
}

Igor,

Tengo otra duda:

Porque utilizas en

void sendMSG(......)
checksum_ACK=address1+address2+5+data_type+code1+code2+Sign+data1+data2+data3+data4+3;
...+5....+3 en esta linea y

void sendACK(....)
checksum_ACK=address1+address2+6+data_type+code1+code2+Sign+data1+data2+data3+data4+3;
...+6......+3 en esta outra?