Autor Tema: Utilizando módulos RF de bajo costo  (Leído 5496 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado elreypic2

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 1297
Utilizando módulos RF de bajo costo
« en: 01 de Noviembre de 2016, 01:54:36 »
Que tal a todos los foreros de este maravilloso foro de microcontroladore PIC y demás.
Con el transcurso del tiempo me he dado cuenta que muchos preguntan ¿cómo usar los módulos de bajo costo como los que se muestran a continuación?

* rf_pair_1000X1000.jpg
(107.94 kB, 1001x1001 - visto 1326 veces)

Hace algún tiempo publiqué algo relacionado con esto, pero al parecer no pudieron implementarlo correctamente. Así que poco a poco iré comentando en este post cada paso.
Es muy importante conocer el máximo baudrate que estos módulos soportan. Este va desde los 1200bps hasta los 4800bps. (Regularmente el mostrado tiene un baud rate de 2400bps).

La idea de este proyecto es hacer una interface de comunicación punto a punto unidireccional. Es decir hay un transmisor y un receptor.

1. Lo primero que debemos hacer es usar algún tipo de codificación para los datos, la cual pudiera ser PWM o Manchester. El uso de NRZ (Non Return To Zero) no es eficiente y ya algunos se han dado cuenta de eso. Para este proyecto he optado por la codificación manchester, ya que esta es fácil de implementar y haremos uso de un UART emulado por software, ya que el dato deberá transmitirse invertido (algunos microcontroladores PIC contienen un USART por hardware con la característica de poder enviar los datos invertidos). Para nuestro proyecto estaremos usando el PIC12F629, que es muy facil de conseguir.
Existe mucha información en la internet acerca de la codificación manchester, pero para resumir podemos decir que esta esta basada en transiciones, en donde una transición de alto a bajo es un "cero" y una transición de bajo a alto es un "1".

https://es.wikipedia.org/wiki/Codificaci%C3%B3n_Manchester

2. Es importante estabilizar el receptor, ya que estos tienen un control automático de ganancia (AGC), por lo que en estado inicial de la ganacia es muy alta produciendo disparos aleatorios (ruido) en el pin de recepción y esto es el dolor de cabeza para todos. Porque lo que realmente esperamos es que el nivel del pin de recepción esté en bajo todo el tiempo, pero esto en la práctica no es posible. Así que lo que vamos a hacer es enviar un tren de pulsos para que el receptor pueda ajustar su ganancia y una vez que lo hace podemos apagar el transmisor por un periodo de tiempo corto de modo que nos pueda servir como un estado estable y entonces enviar los datos.
Este es el esquemático que usaremos para nuestro proyecto:


* TX_RX.JPG
(124.75 kB, 1037x540 - visto 1319 veces)


Como lo mencione en el parrafo anterior, necesitamos enviar un tren de pulsos de modo que el receptor pueda ajustar su ganancia. Lo que yo hago es enviar 16 veces el byte 0xF0:

* TX_RX_Preamble.JPG
(85.59 kB, 701x451 - visto 1283 veces)

En la imagen anterior solo se pueden apreciar 9 de los 15 bytes con valor 0xFA.
El código en C sería algo como esto:
Código: C
  1. for (i=0; i<16; i++)
  2.       {
  3.          fputc(0xF0,RFPORT);    //Envia 16 bytes de inicio
  4.       }

Una vez transmitidos estos 16 bytes de preámbulo o inicio, enviaremos 3 bytes de encabezado o de reconocimiento. Mi sugerencia, debido a mi experiencia con estos módulos, es enviar 0xF8, 0xF8 y 0xFA

El código sería:

Código: C
  1. fputc(0xF8,RFPORT);  //Envia bytes de reconocimiento
  2.       fputc(0xF8,RFPORT);
  3.       fputc(0xFA,RFPORT);





« Última modificación: 04 de Noviembre de 2016, 06:29:34 por elreypic2 »

Desconectado elreypic2

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 1297
Re:Utilizando módulos RF de bajo costo
« Respuesta #1 en: 04 de Noviembre de 2016, 06:52:34 »
La siguiente imagen muestra los bytes de encabezado o los bytes de reconocimiento:

* TX_RX_Header.JPG
(86.91 kB, 698x453 - visto 1246 veces)

En la simulación he colocado una terminal en el pin de transmisión para visualizar los bytes que son enviados y la siguiente figura muestra dichos datos:

* TX_RX_Datos.JPG
(21.93 kB, 455x256 - visto 1240 veces)


Como se puede apreciar, en la primera linea de la terminal se muestran los 16 bytes de preambulo cuyou valor es 0xF0.
En la segunda linea, se puede apreciar que los primeros 3 bytes son el encabezado.

Ahora bien volviendo al tema de la codificación manchester, hemos visto que para codificar un bit se necesita de una transición, la cual para un "1" es una transición de 1 a 0 y para un "0" es una transición de 0 a 1. Esto quiere decir que un byte codificado en manchester producirá dos bytes, nuestra función sería como sigue:

Código: C
  1. void SEND_DATA(BYTE txbyte)
  2. {
  3.     int i,j,b,me;  
  4.     b = txbyte;  
  5.     for (i=0; i<2; i++)
  6.     {
  7.         me = 0;         // manchester encoded txbyte
  8.         for (j=0 ; j<4; j++)
  9.         {
  10.             me >>=2;
  11.             if (bit_test(b,0))
  12.                 me |= 0b01000000; // 1->0
  13.             else
  14.                 me |= 0b10000000; // 0->1
  15.             b >>=1;
  16.         }
  17.         fputc(me,RFPORT);        
  18.     }
  19. }

En el proyecto he colocado una terminal, la cual es usada para proporcionar las datos a ser transmitidos, esta pudiera ser una PC o un microcontrolador. La cual esta configurada con un baudrate de 9600bps, 8 bits de datos, sin paridad e invertido.
Esto quiere decir que estamos usando dos puertos seriales, uno para recibir los datos a transmitir y otro para enviar los datos codificados via RF.
Entonces el código para configurar nuestros puertos seriales sería:

Código: C
  1. #use rs232(baud=2400,parity=N,xmit=PIN_A1,bits=8,invert,STREAM=RFPORT) //Puerto serial para transmitir los datos via RF
  2. #use rs232(baud=9600,parity=N,xmit=PIN_A4,rcv=PIN_A2,bits=8,invert,STREAM=RS232)

Ahora bien, lo que haremos es esperar a que se reciba un dato o datos (hasta un máximo de 32) y para ellos necesitaremos un "array" y una variable que contenga la longitud de los datos a transmitir.

Código: C
  1. int8  i, data_tx[32], lenght;

Algo que también haremos será que el UART esperara hasta un máximo de 65ms entre dato y dato, si no llega un dato en ese tiempo entonces se transmitirá el o los datos previamente recibidos. Para esto utilizaremos el Timer 0 configurado con un prescalar de 256.

Código: C
  1. setup_timer_0(RTCC_INTERNAL|RTCC_DIV_256|RTCC_8_bit); //Configura Timer0 con prescalar 1:256

Nuestro código para la captura de los datos a transmitir quedaría de la siguiente manera:

Código: C
  1. lenght = 0;    //Inicializa la longitud del frame
  2.       data_tx[lenght] = fgetc(RS232);     //Recibe el primer dato
  3.       lenght++;   //Incrementa el valor de la longitud del frame
  4.       T0IF = 0;
  5.       TMR0 = 0;
  6.       while (lenght<32 && !T0IF)
  7.       {
  8.          if (kbhit(RS232))
  9.          {
  10.             data_tx[lenght] = fgetc(RS232);  
  11.             lenght++;
  12.             T0IF=0;
  13.             TMR0=0;
  14.          }
  15.       }

Uniendo todo lo anterior podemos entonces decir que nuestro código final para el transmisor quedaría de la siguiente manera:

Código: C
  1. #include <12F629.h>
  2.  
  3. #FUSES NOWDT                    //No Watch Dog Timer
  4. #FUSES INTRC_IO                 //Internal RC Osc, no CLKOUT
  5. #FUSES NOCPD                    //No EE protection
  6. #FUSES NOPROTECT                //Code not protected from reading
  7. #FUSES NOMCLR                   //Master Clear pin used for I/O
  8. #FUSES PUT                      //Power Up Timer
  9. #FUSES BROWNOUT                 //Reset when brownout detected          
  10. #FUSES RESERVED                 //Used to set the reserved FUSE bits
  11.  
  12. #use delay(clock=4000000)
  13. #use rs232(baud=2400,parity=N,xmit=PIN_A1,bits=8,invert,STREAM=RFPORT)
  14. #use rs232(baud=9600,parity=N,xmit=PIN_A4,rcv=PIN_A2,bits=8,invert,STREAM=RS232)
  15.  
  16. #byte TMR0=0X01
  17. #byte INTCON=0X0B
  18. #bit  T0IF=INTCON.2
  19.  
  20.  
  21. void SEND_DATA(BYTE txbyte)
  22. {
  23.     int i,j,b,me;  
  24.     b = txbyte;  
  25.     for (i=0; i<2; i++)
  26.     {
  27.         me = 0;         // manchester encoded txbyte
  28.         for (j=0 ; j<4; j++)
  29.         {
  30.             me >>=2;
  31.             if (bit_test(b,0))
  32.                 me |= 0b01000000; // 1->0
  33.             else
  34.                 me |= 0b10000000; // 0->1
  35.             b >>=1;
  36.         }
  37.         fputc(me,RFPORT);        
  38.     }
  39. }
  40.  
  41.  
  42. void main()
  43. {
  44.    int8  i, data_tx[32], lenght;
  45.    setup_comparator(NC_NC);
  46.    setup_vref(FALSE);
  47.    setup_timer_0(RTCC_INTERNAL|RTCC_DIV_256|RTCC_8_bit);
  48.    while (1)
  49.    {
  50.       lenght = 0;    //Inicializa la longitud del frame
  51.       data_tx[lenght] = fgetc(RS232);     //Recibe el primer dato
  52.       lenght++;   //Incrementa el valor de la longitud del frame
  53.       T0IF = 0;
  54.       TMR0 = 0;
  55.       while (lenght<32 && !T0IF)
  56.       {
  57.          if (kbhit(RS232))
  58.          {
  59.             data_tx[lenght] = fgetc(RS232);  
  60.             lenght++;
  61.             T0IF=0;
  62.             TMR0=0;
  63.          }
  64.       }
  65.       for (i=0; i<16; i++)
  66.       {
  67.          fputc(0xF0,RFPORT);    //Envia 16 bytes de inicio
  68.       }
  69.       fputc(0xF8,RFPORT);  //Envia bytes de reconocimiento
  70.       fputc(0xF8,RFPORT);
  71.       fputc(0xFA,RFPORT);
  72.       SEND_DATA(lenght);   //Envia la longitud de frame
  73.       for (i=0; i<lenght; i++)
  74.       {
  75.          SEND_DATA(data_tx[i]);  //Envia los datos
  76.       }
  77.          
  78.    }
  79. }

En la siguiente entrega veremos la parte del receptor y la decodificación manchester.
Cualquier pregunta por favor no duden en hacerla y sea mediante este tema o en mensaje privado.

Bendiciones,

elreypic.
« Última modificación: 04 de Noviembre de 2016, 07:11:44 por elreypic2 »

Desconectado elreypic2

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 1297
Re:Utilizando módulos RF de bajo costo
« Respuesta #2 en: 12 de Noviembre de 2016, 22:15:31 »
Que tal amigos foreros. Me disculpo por la tardanza para esta segunda parte de este proyecto. Sin más preámbulos continuemos.

Tal como se habrán ya dado cuenta, el protocolo de comunicación es como sigue:

16 bytes de preámbulo (0xF0) + 3 Byte Header (0xF8, 0xF8, 0xFA) + 2 bytes codificado en manchester de la longitud de los datos + datos codificados en manchester.

Pues bien para la recepción también vamos a usar un PIC12F629 y vamos a necesitar 2 puertos seriales, uno para la recepción RF y otro para mostrar los datos recibidos (RS232). El puerto serial para la recepción RF configurado a 2400 bps, 8 bits, no paridad e invertido.
El puerto RS232 a 9600 bps, 8 bits, no paridad e invertido (para evitar convertidor de TTL a RS232).

El segmento del código quedaría así:

Código: C
  1. #use rs232(baud=2400,parity=N,rcv=PIN_A1,bits=8,STREAM=RFPORT,invert)
  2. #use rs232(baud=9600,parity=N,xmit=PIN_A0,bits=8,STREAM=RS232,invert)

Para la recepción debemos ignorar los bytes de preámbulo, lo importante es esperar los bytes de header. Nuestro código para esperar y evaluar el header sería así:

Código: C
  1. do
  2.       {
  3.          do
  4.          {
  5.             do
  6.             {
  7.                rx_data = fgetc(RFPORT);   //Recibe el primer dato via RF
  8.             } while (rx_data!=0xF8);   //Si el dato recibido no es 0XF8, continua recibiendo datos
  9.             rx_data = fgetc(RFPORT);   //Recibe el segundo dato
  10.          } while (rx_data!=0xF8);   //Si el dato recibido no es 0XF8, regresa al inicio y espera nuevo dato
  11.          rx_data = fgetc(RFPORT);   //Recibe el tercer dato
  12.       } while (rx_data!= 0xFA);     //Si el dato recibido no es 0XFA, regresa al inicio y espera nuevo dato

Una vez que se ha recibido el header, tendremos que recibir la longitud de los datos, pero recordemos que esta está codificada en manchester, por lo tanto necesitamos la subrutina para decodificar el manchester. Esta subrutina sería así:

Código: C
  1. BYTE DECODE_DATA(int8 encoded)  //decode function for manchester encoded data.
  2. {
  3.    int8 enc, dec, pattern, i;
  4.    enc = encoded;
  5.        
  6.     dec = 0;    
  7.     for (i=0; i<4; i++) {
  8.    
  9.         dec >>=1;
  10.         pattern = enc & 0b11;        
  11.         if (pattern == 0b01)        // 1
  12.            bit_set(dec,3);
  13.         else if (pattern == 0b10)
  14.            bit_clear(dec,3);       // 0
  15.         else
  16.             return 0xff;            // illegal code
  17.            
  18.         enc >>=2;  
  19.     }    
  20.  
  21.     return (dec);
  22. }

Ahora bien la longitud de los datos está codificada en manchester y viene en 2 bytes, para recibirla y decodificarla, nuestro código sería así:

Código: C
  1. nib[0] = fgetc(RFPORT);    //Recibe la primera parte baja de la longitud del frame a recibir
  2.       dec1 = DECODE_DATA(nib[0]);   //Decodifica la parte baja de la longitud
  3.       nib[1] = fgetc(RFPORT);    //Recibe la parte alta de la longitud
  4.       lenght = DECODE_DATA(nib[1]); //Decodifica la parte alta de la longitud del frame
  5.       lenght<<=4;    //Posiciona la parte alta de la longitud
  6.       lenght |= dec1;   //Ensambla la longitud del frame

Una vez que tenemos la longitud de los datos, entonces pasamos a recibir dichos datos:
Código: C
  1. for (i=0; i<lenght; i++)   //Prepara para recibir los datos via RF
  2.       {
  3.          nib[0]=fgetc(RFPORT);      //Recibe la parte baja del dato
  4.          dec1=DECODE_DATA(nib[0]);  //Decodifica la parte baja del dato
  5.          nib[1]=fgetc(RFPORT);      //Recibe la parte alta del dato
  6.          data_rx[i]=DECODE_DATA(nib[1]);  //Decodifica la parte alta del dato
  7.          data_rx[i]<<=4;   //Coloca el nible alto en su posicion correcta
  8.          data_rx[i]|=dec1;    //Ensambla el dato recibido
  9.       }

Una vez recibidos los datos, pasamos a mostrarlos en la PC, enviándolos via RS232:

Código: C
  1. for (i=0;i<lenght;i++)     //Prepara para enviar los datos recibidos
  2.       {
  3.          fputc(data_rx[i],RS232);   //Envia el dato via RS232
  4.       }

Poniendo todo junto, nuestro código final quedaría de la siguiente manera:

Código: C
  1. #include <12F629.h>
  2.  
  3. #FUSES NOWDT                    //No Watch Dog Timer
  4. #FUSES INTRC_IO                 //Internal RC Osc, no CLKOUT
  5. #FUSES NOCPD                    //No EE protection
  6. #FUSES NOPROTECT                //Code not protected from reading
  7. #FUSES NOMCLR                   //Master Clear pin used for I/O
  8. #FUSES PUT                      //Power Up Timer
  9. #FUSES BROWNOUT                 //Reset when brownout detected          
  10. #FUSES RESERVED                 //Used to set the reserved FUSE bits
  11.  
  12. #use delay(clock=4000000)
  13. #use rs232(baud=2400,parity=N,rcv=PIN_A1,bits=8,STREAM=RFPORT,invert)
  14. #use rs232(baud=9600,parity=N,xmit=PIN_A0,bits=8,STREAM=RS232,invert)
  15.  
  16. int8  nib[2], i, dec1, rx_data, lenght, data_rx[32];
  17.  
  18. BYTE DECODE_DATA(int8 encoded)  //decode function for manchester encoded data. This code is working fine. I checked it.
  19. {
  20.    int8 enc, dec, pattern, i;
  21.    enc = encoded;
  22.        
  23.     dec = 0;    
  24.     for (i=0; i<4; i++) {
  25.    
  26.         dec >>=1;
  27.         pattern = enc & 0b11;        
  28.         if (pattern == 0b01)        // 1
  29.            bit_set(dec,3);
  30.         else if (pattern == 0b10)
  31.            bit_clear(dec,3);       // 0
  32.         else
  33.             return 0xff;            // illegal code
  34.            
  35.         enc >>=2;  
  36.     }    
  37.     return (dec);
  38. }
  39.  
  40.  
  41. void main()
  42. {
  43.    setup_comparator(NC_NC);
  44.    setup_vref(FALSE);
  45.    fprintf(RS232,"RF Link\n\r"); //Envia el mensaje "RF Link"
  46.    while (1)
  47.    {
  48.       do
  49.       {
  50.          do
  51.          {
  52.             do
  53.             {
  54.                rx_data = fgetc(RFPORT);   //Recibe el primer dato via RF
  55.             } while (rx_data!=0xF8);   //Si el dato recibido no es 0XF8, continua recibiendo datos
  56.             rx_data = fgetc(RFPORT);   //Recibe el segundo dato
  57.          } while (rx_data!=0xF8);   //Si el dato recibido no es 0XF8, regresa al inicio y espera nuevo dato
  58.          rx_data = fgetc(RFPORT);   //Recibe el tercer dato
  59.       } while (rx_data!= 0xFA);     //Si el dato recibido no es 0XFA, regresa al inicio y espera nuevo dato
  60.      
  61.       nib[0] = fgetc(RFPORT);    //Recibe la primera parte baja de la longitud del frame a recibir
  62.       dec1 = DECODE_DATA(nib[0]);   //Decodifica la parte baja de la longitud
  63.       nib[1] = fgetc(RFPORT);    //Recibe la parte alta de la longitud
  64.       lenght = DECODE_DATA(nib[1]); //Decodifica la parte alta de la longitud del frame
  65.       lenght<<=4;    //Posiciona la parte alta de la longitud
  66.       lenght |= dec1;   //Ensambla la longitud del frame
  67.       for (i=0; i<lenght; i++)   //Prepara para recibir los datos via RF
  68.       {
  69.          nib[0]=fgetc(RFPORT);      //Recibe la parte baja del dato
  70.          dec1=DECODE_DATA(nib[0]);  //Decodifica la parte baja del dato
  71.          nib[1]=fgetc(RFPORT);      //Recibe la parte alta del dato
  72.          data_rx[i]=DECODE_DATA(nib[1]);  //Decodifica la parte alta del dato
  73.          data_rx[i]<<=4;   //Coloca el nible alto en su posicion correcta
  74.          data_rx[i]|=dec1;    //Ensambla el dato recibido
  75.       }
  76.       for (i=0;i<lenght;i++)     //Prepara para enviar los datos recibidos
  77.       {
  78.          fputc(data_rx[i],RS232);   //Envia el dato via RS232
  79.       }
  80.      
  81.    }  
  82. }

La siguiente es una imagen de un dato transmitido ("a"):

* a_data.JPG
(17.81 kB, 452x252 - visto 1157 veces)


La siguiente imagen es el frame transmitido via RF:


* a_coded.JPG
(22.01 kB, 453x257 - visto 1214 veces)


Y finalmente la imagen en donde se recibe el caracter "a"

* a_RX.JPG
(17.41 kB, 454x258 - visto 1177 veces)


Espero esto les pueda servir y cualquier duda o pregunta por favor no duden en hacerla.

Elreypic.

Desconectado pachopic

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 209
Re:Utilizando módulos RF de bajo costo
« Respuesta #3 en: 06 de Diciembre de 2016, 19:44:29 »
Hola

muchas Gracias por la amplia explicación y aporte.

tengo una pregunta como le haces para simular el  transmisor y receptor en proteus?
donde puedo encontrar la librería?  cual es  esta versión?

Salu2

Desconectado elreypic2

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 1297
Re:Utilizando módulos RF de bajo costo
« Respuesta #4 en: 06 de Diciembre de 2016, 21:38:18 »
Que tal pachopic,

La librería para los modulos de transmision-recepcion los puedes descargar del siguiente link:
http://microcontrolandos.blogspot.com/2013/10/pic-modulo-rf.html

Saludos,

elreypic.

Desconectado jeremylf

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 1341
Re:Utilizando módulos RF de bajo costo
« Respuesta #5 en: 20 de Diciembre de 2016, 20:37:18 »
Buena iniciativa  ((:-))

Desconectado pibe

  • Colaborador
  • PIC24F
  • *****
  • Mensajes: 635
Re:Utilizando módulos RF de bajo costo
« Respuesta #6 en: 22 de Diciembre de 2016, 18:09:32 »
Si hubiese tenido toda esta info hace 10 años atrás te hubiese besado los pies. XD
Excelente proyecto, bien documentado y prolijo. Mis felicidades.
Mi jefe mirando el prototipo que estoy creando: "Y eso va a funcionar?"