Buses de campo para Arduino.... RS485

.
.
Originalmente, toda la información la publiqué en el foro de Arduino en español. Puedes seguirlo aquí.

El RS485 es un estandar de comunicación serie en el cual se pueden tener varios dispositivos en topología de bus y de una manera muy económica. Aquí explicaremos brevemente como realizar una pequeña red basada en 2 hilos.
Dado que sólo se usan dos hilos balanceados (diferencial), las comunicaciones son semiduplex. Para configurar el driver como transmisor o receptor, el driver dispone de una entrada digital para su configuración.

Para darle conectividad RS485 a nuestro arduino, sólo necesitamos de un convertidor de niveles para usar la UART del micro, y por menos de 1 euro tendremos todo lo necesario.
Los chips más usados son:
- Max485
- SN75176 (siendo éste el más económico)




La norma RS485 sólo establece las características físicas, no definiendo ningún protocolo ó forma de conexionado.

Señalamos unos links muy recomendables:
http://www.i-micro.com/pdf/articulos/rs-485.pdf
http://www.maxim-ic.com/appnotes.cfm?appnote_number=763&CMP=WP-1
http://www.neoteo.com/rs485-domotica-al-alcance-de-tu-mano-15810.neo
Documento con tramas de ejemplo para solucionar problemas
http://www.sbc-support.ch/faq/files/files.get.php?ZSESSIONID=n38bphov4ls4cdsvtbd...



La manera más sencilla de aprender como realizar un protocolo, es basarte en algo que ya existe. Para ello, me he basado en el protocolo profesional usado por Fuji para sus variadores industriales.
Véase: http://www.cdautomation.com/download/ENG_L_M_FUJI_RS485_COMM_for_FRENIC-Mini.PDF

Veamos un diagrama explicativo de la trama:



Las tramas son de 15 bytes:
  • Byte 1: Byte de start ( 0 hexadecimal ).
  • Byte 2-3: SCII 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)

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,… Elección del diseñador!!!
Se puede observar que los comandos están en ASCII y los bytes de control no. Esto ayuda enormente en la programación, ya que si recibimos 0x00 (suponiendo que es el byte de start), nuestro programa sabe que es un inicio de trama y no lo entiende como un "0" (número cero), ya que éste se enviaría en ASCII (0x30).
A continuación, adjunto una tabla ASCII sacada de http://www.cs.utk.edu/~pham/ascii_table.jpg





Veamos un ejemplo:
Queremos que un arduino (maestro) envíe la orden de encendido/apagado de un led conectado a otro arduino arduino (esclavo). El interruptor está en el maestro y la actuación (encendido/apagado) en el esclavo.

Según el estado del interruptor, el maestro envía la trama de petición de encendio/apagado, al esclavo ,cuya dirección es 01. Se ha decidido que la función se llamará D y el número 00 (completamente configurable).
Ej: 0x00 0x30 0x31 0x05 0x44 0x30 0x30 0x20 0x30 0x30 0x30 0x31 0x03 0x01 0xEE
El esclavo, contesta con un ACK (byte 4) al maestro confirmando el dato recibido.
Ej: 0x00 0x30 0x31 0x06 0x44 0x30 0x30 0x20 0x30 0x30 0x30 0x31 0x03 0x01 0xEF

Mejor,lo vemos con un video: http://www.youtube.com/watch?v=ABcjU0Ua-d4


(el interruptor es un triste cable que lo llevo a masa o VCC.... sorry!!!)

Para poder ver las tramas, bastará un conversor USB <--> RS232 basado en chip ftdi, ó un MAX232+MAX485,... Si tienes una placa Arduino de sobra, quitale el micro y tendrás un conversor USB --> RS232.



 
El software para monitorizar lo que está pasado, puede ser el hyperterminal de windows ó alguno gratuito que añaden mejoras como poder ver los datos en hexadecimal, grabar,...:
RealTerm
FREE SERIAL TERMINAL MONITOR


Ahora veamos un ejemplo con 3 arduinos interconectados. El mestro dispone de 2 switch, los cuales comandan el encendido/apagado de un led conectado a cada uno de los esclavos.




Veamos el video de funcionamiento: http://www.youtube.com/watch?v=S9FSQaToVZ4




Ahora toca el código:

IMPORTANTE: El checksum lo hago directamente en hexadecimal en vez de convertirlo a ASCII. Se debería cambiar el código, para no tener problemas. Esta publicación se trata de un acercamiento de como manejar el bus rs485 y explicar como implementar un protocolo, por lo que no está 100%.




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;  
}



 

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;  
}

Ahora veamos como cablear una red de topología bus:

 
En cada Arduino o dispositivo de la red, se cablea con una Y soldada en la cual salen dos conectores de la misma familia. Debe tener la longitud más corta posible.
Cada conector tiene Alimentación, Señal A, Señal B y Masa.

De esta forma, si se quiere ir ampliando la red con dispositivos, bastará con ir intercalandolos ó poniendolos a final. Ya que cada dispositivo tiene conectores macho y hembra, por lo que encaja perfecto.
Se puede preparar un par de conectores, con una resistencia terminadora puesta en los terminales correspondientes a la Señal A y Señal B del bus RS485 para cerrar los extremos (siempre quedará un par de conectores libres). Si se necesita añadir más dispositivos, se desconecta dicho conector con la resistencia terminadora, se intercala el nuevo dispostivo y se vuelve a cerrar la red.

Llevar alimentación en el cableado, no cuesta nada y todo será más fácil para futuras ampliaciones...
El cable de Señal A y B debe ser trenzado.
Recomiendo este link acerca del conexionado: http://www.rs-485.com/download/485%20network%20topology.pdf 
.
.
.
.
Saludos!!
.
Igor R.
.
.

34 comentarios:

  1. Excelente iniciativa.
    Cristal

    ResponderEliminar
  2. Hola Igor, muchas gracias por tan currado articulo. Tengo que conectar mi arduino duemilanove con un "analizador de red" llamado "circuitor", éste lleva un rs485 y usa protocolo modbus. Así que lo que necesiro es dar soporte al arduino para rs485, el problema es que no tengo mucha idea de electronica. He visto en tu articulo que usas el chip max485, pero no sé como lo conectas en la placa de prototipado ni como lo conectas al arduino. ¿puedes charme un mano?

    ResponderEliminar
  3. Hola Felix!!

    Voy a poner el pin-out con una placa diecimila.

    PIN-OUT MAX485
    1 -> RO (Receiver Output) al pin 0 (Rx)
    2 -> Receive enable a un pin digital (ej: pin2)
    3 -> Driver enable al pin digital (ej:pin2)
    4 -> DI (Driver In) al pin 1 (Tx)
    5 -> GND a gnd de arduino
    6 -> SEÑAL A del bus RS-485
    7 -> SEÑAL B del bus RS-485
    8 -> Vcc a 5 voltios de arduino


    Saludos

    ResponderEliminar
  4. Muchísimas gracias!, acabo de pedir 2 max485, mañana me pondré con ello. Como de momento no tengo el "circuitor" para probar que funciona voy a montar 2 arduino + max485 y enviar tramas entre ellos a ver si soy capaz, para lo bueno o la malo te lo hago saber, espero que sea para bien :)

    ResponderEliminar
  5. Hola Felix,

    Si tienes un conversor de FTDI lo puedes usar junto a un MAX485 para tener una especie de sniffer en el ordenador. Dispone de una patilla (TXDEN) que controla envio/recepción en las comunicaciones half-duplex.

    Otra forma,si tienes dos placas diecimila ó duemilanove, lo que puedes hacer a una de ellas le quitas el micro y lo usas como conversor USB <-> Serie. Al añadirle el Max485, podrás usar el RealTerm, hyperterminal ó similar para ver lo que está ocurriendo en el bus.(Sólo recepción).

    Si en el ordenador dispones de puerto serie, con un max232 y el max485 también te vale.

    Es una forma más sencilla para depurar, y más sencilla de empezar, que intentar comunicar directamente dos Arduinos. Asi haces una comunicacion Arduino <-> PC mediante rs485. Cuando tengas el "circuitor" podrás ver que es lo que ocurre en el bus con tu PC.

    Una vez que tengas dominado la parte física, te queda implementar el protocolo modbus rtu en tu Arduino.(http://www.modbus.org y http://freemodbus.berlios.de/)


    Saludos

    ResponderEliminar
  6. Hola Igor, ya estoy intentando que esto funcione.
    Para probarlo lo que quiero hacer es enviar algo, cualquier cosa de un arduino a otro y activar un led cuando lo reciba
    Te digo las conexiones que he hecho:

    1 -> RO (Receiver Output) -> pin 0 (Rx)
    2 -> Receive enable -> pin2
    3 -> Driver enable -> pin3
    4 -> DI (Driver In) -> pin 1 (Tx)
    5 -> GND a gnd de arduino
    6 -> SEÑAL A del bus RS-485 Conectada a señal A del otro chip
    7 -> SEÑAL B del bus RS-485 conectada a la señal B del otro chip
    8 -> Vcc a 5 voltios de arduino

    Aquí están las fotos:
    http://picasaweb.google.es/jaaaelpumuki/Arduino?feat=directlink

    Lo que no tengo claro es porque usan por ejemplo el pin13 en maestro, o porque tienes un pin de control y 2 de estado(8 y 9) o es que los de estado 8 y 9 son el A y el B del chip?

    ResponderEliminar
  7. Hola Felix,

    Una cosa que a primera vista he visto. A y B deben estar trenzados entre si y debe haber resistencia terminadora en ambos extremos.
    El pin2 y pin3 puedes llevarlos al mismo pin de Arduino, ya que trabajan con lógica inversa, por lo que juntandolos ya tienes lo que buscas (le he llamado pinCONTROL=2). En "High" habilitará el driver como transmisor y "Low" como receptor.

    Los pines 8 y 9 son las entradas de los botones, que están enchufados al Maestro y controlan el encendido/apagado en el Esclavo. Es decir, cuando hay un cambio en esa input, le envio la orden correspondiente al esclavo.

    El pin 13 es donde tengo el led en los esclavos.



    Saludos

    ResponderEliminar
  8. muchas gracias! qué es exactamente una resistencia terminadora. Aquí tengo 2 resistencias una de 10 y de 2 de 1.

    ResponderEliminar
  9. Hola Felix,

    Te recomiendo leer este link: http://www.maxim-ic.com/appnotes.cfm?appnote_number=763&CMP=WP-1

    La resistencia terminadora se debe poner, para que no ocurra lo mismo que en las antenas (ondas estacionarias) ó por ejemplo, cuando alumbras con un foco al agua... Si tienes un cambio de impedancia, parte de la onda es reflejada.

    Tienes que poner 100 ó 120 ohm.


    Saludos

    ResponderEliminar
  10. Te paso fotos de las conexiones por separado a ver si hay algo mal, porque no tengo ni idea de que pasa:
    - A y B resistencia en paralelo y cable entrelazado:
    http://picasaweb.google.es/lh/photo/eGRtcDgH6glUd-dI4Al6NQ?feat=directlink
    http://picasaweb.google.es/jaaaelpumuki/Arduino#5403538363171657090

    - RE y DE unidos con pin 2 de arduino:
    http://picasaweb.google.es/lh/photo/Pnderl5aGhSBQOXQOf9I-w?feat=directlink

    - Gnd con gnd y Vcc con 5v arduino:
    http://picasaweb.google.es/lh/photo/Rzud7VMsumvcDMpCqYLZbg?feat=directlink
    http://picasaweb.google.es/lh/photo/ZSoKf1pW8juquqzcpv4qsQ?feat=directlink

    - DI con pin 1 TX y R0 con pin 0 RX:
    http://picasaweb.google.es/lh/photo/KfDrnlb0nFpTh_o_txFcKg?feat=directlink
    http://picasaweb.google.es/lh/photo/KfDrnlb0nFpTh_o_txFcKg?feat=directlink

    ResponderEliminar
  11. No me cuadra nada!!! je,je,je
    A y B son pines 6 y 7
    Re y De son pines 2 y 3
    ....

    Revisa todo!!! Mira la foto del datasheet del Max485 para que veas como van los pines=> http://www.maxim-ic.com/images/qv/1111.gif

    Si ves el componente, y teniendo la marca que tiene el encapsulado, los pines van empezando por la izquierda el 1,2,3,4 y en la derecha desde abajo hacia arriba 5,6,7 y 8.

    ResponderEliminar
  12. A y B son pines 6 y 7, 6 y 7 del arduino o del max485?
    Y RE y DE no tenían que ir unidos y luego al pin 2 del arduino?

    , yo el esquema que tengo del max es este:
    http://picasaweb.google.es/jaaaelpumuki/Arduino#5403242571437031314

    ResponderEliminar
  13. Pero tu tienes encapsulado DIP, no µMAX...
    Fijate en el link que te di, que vienen los dos... pero uno es para encapsulado DIP y otro µMAX. Son diferentes los pin-out. En la web de maxim puedes verlo...

    ResponderEliminar
  14. opppssss :):):) joer era eso!!!!!. ya está!!! ahora a por ModBus!!! Mil gracias!!

    ResponderEliminar
  15. Me alegro!!! Informa como vas con tu proyecto de Modbus....

    ResponderEliminar
  16. Si quieres hacer pruebillas de MODBUS RTU a mi esta aplicación me va de vicio.
    http://sites.google.com/site/plcsimulator/

    Yo lo utilizo como esclavo en integración de sistemas, para debuggear mi protocolo cuando ya lo he implementado sobre la plataforma y va de vicio.

    Siempre puedes, como dice Igor, primero afianzar que has desarollado bien el protocolo MODBUS, y luego dejar a los arduinos que se digan cosillas :P

    Un saludo!

    P.D. Mandanos si te apetece colaborar la implementación MODBUS y la publicamos por aquí.

    ResponderEliminar
  17. Que cambio le hago al programa para poder enviar una lectura de un potenciometro en los esclavos y recibirlos en el maestro.

    Muchas gracias

    ResponderEliminar
  18. Hola igor que es el circuito que esta en color negro en la placa donde esta el max485???

    ResponderEliminar
  19. Te refieres al jumper?? Es para añadir o no la resistencia terminadora.

    ResponderEliminar
  20. Hola Igor, felicidades!! no solo tu proyecto es interesante sino también aplicable y funcional en diferentes áreas. Te felicito y agradezco tu atención para todos tus seguidores
    por el momento estoy involucrada en un proyecto de una red de comunicación basada en el estándar RS485 y estoy comenzando en esto de las comunicaciones, tengo algunas dudas q seguro me podrás orientar.
    Cuando haces mención del protocolo FUJI es simplemente para tomar su estructura de trama como una referencia??
    La propuesta de un protocolo de comunicación sigue reglas específicas si será utilizado para RS485 o Rs232??

    ResponderEliminar
  21. Hola,
    Gracias!!
    He usado un protocolo como orientación, ya que me parecía que estaba muy bien.
    El RS485 sólo define la capa física, por lo que es el usuario quien tiene que hacer todas las "reglas" (protocolo) para intercambiar la información entre los diferentes dispositivos de la red RS485.
    Algún otro ejemplo de protocolo es MODBUS.

    El protocolo es algo soft, por lo que puedes utilizarlo en rs232 también.


    Saludos

    ResponderEliminar
  22. Hola Igor,
    Excelente aporte. Voy aplicarlo como Modbus en un enlace Arduino --> Wincc flexible de Siemens y necesito saber si tienes algo ya probado.
    Te lo pregunto porque he visto que trabajaste con automatas por ahí no?.
    Saludos

    ResponderEliminar
  23. No tengo nada, asi que estaria genial si pudieses documentar tus progresos. Los podria publicar por aqui si quieres.
    Salu2

    ResponderEliminar
  24. OK, Igor , acepto, publicalo. Solo necesitaré mucha ayuda y horas... Je,Je. Otros dedican el tiempo libre a la Cerveza,etc. Bueno he comenzado intentando implementar parte de tu proyecto Excel arduino a Wincc, porque sabes que dicha aplicación tiene Vbscript, pero no me reconoce algunos comandos como NETCOMM2.
    Primera cuestión, has visto algo de Wincc?

    ResponderEliminar
  25. También te comento que en el propio soft, aparecen unas conexiones tipo modicon Modbus seleccionas hay rs 485. Por lo tanto, otra puerta y otra cuestión. Es compatible este protocolo que has diseñado con modicon Modbus 485.
    Permite hasta 19200,par,8,1. Je,je.
    La trama dice que es RTU Standar.

    ResponderEliminar
  26. Hola,

    me gustaría saber para que sirve el bit se signo.

    Gracias.

    ResponderEliminar
  27. Hola Igor,
    Gracias por tu aporte, me esta sirviendo de gran ayuda pero tengo dos dudas en el código.
    1- if (times_repeat<4) yo entiendo que este apartado se ejecuta 5 veces y no se el porque (perdona mi ignorancia pero es que soy nuevo)

    2- while (((millis()-previous_time)<500) && (Serial.available()!=15)){ ;;} No se para que funciona.

    Un saludo

    ResponderEliminar
  28. Hola Lluis,

    La idea es que cuando mandas un mensaje, esperas la respuesta de tu esclavo diciendo que todo ha ido bien. Este mensaje es el ACK. Entonces tienes que tener mecanismo para asegurarte que se recibe el mensaje. Uno sencillo es mandarlo y estar esperando la respuesta. Necesitas tener un timeout, es decir el tiempo máximo que se queda esperando a que dicha respuesta llegue, antes de volver a mandar otra vez el mensaje.
    Entonces en el ejemplo, si se recibe un mensaje que no es el ACK o no se recibe nada, se intenta 5 veces más.
    Todos estos mecanismos se pueden mejorar, pero es un ejemplo básico.

    Espero que quede más claro.

    Saludos.

    ResponderEliminar
  29. Gracias Igor, me has resuelto las dudas. Ahora se que el while es necesario.

    Un saludos Lluis

    ResponderEliminar
  30. Hola Igor, esta muy bueno este post, pero tengo una duda, yo ahorita he intentado comunicar los atmega sin utilizar las placas Arduino, los he conectado directo (Tx del maestro al Rx Esclavo) y nomas no da nada, ¿Tendrá algo que ver las placas Arduino? y la otra, ¿el rs485 es necesario para comunicar los atmega? como apenas estoy empezando pues se me hace mucha duda. Gracias y Saludos!

    ResponderEliminar
  31. Hola Igor,

    muchas gracias por documentar tan bien el trabajo.

    Soy nuevo en todo esto de los bites y los bytes y no consigo convertir un variable float en un array de 4 bytes que pueda asignar a los 4 bytes de datos de la trama.

    Esto me sería muy útil para poder recoger datos de sensores!


    Gracias!

    ResponderEliminar
  32. Muy bueno tu articulo, ayudo mucho...
    Ahora lo que quiero hacer es que los 3 arduinos sean master y esclavo a la vez (es decir, que reciban y envien datos, cualquier dato). Esto es posible? No encuentro nada por Google...

    ResponderEliminar
  33. Hola que tal ...
    Es muy interesante tu trabajo sobre todo porque ayuda a muchas personas
    me gustaria saber si podrias pasarme los diagramas de conexion y algunos
    ejemplos con los cuales pueda probar te lo agradeceria de ante mano :D

    btk_20@hotmail.com

    Julio

    ResponderEliminar