bclose

Comunicación dúplex con NRF2401

Enviando y recibiendo datos por RF con nuestros Arduinos

Objetivos

 

 
    • Seguimos probando los módulos de radio NRF2401.
    • Vemos porque no podemos enviar y recibir a la vez con ellos.
    • Presentar un par de programas sencillos que nos permitan usarlos en nuestros proyectos para enviar y recibir órdenes o datos en ambas direcciones y en ambos módulos.
 
 

Material requerido.

Imagen de Arduino UNO

Arduino UNO o equivalente.

Transceptor de radio

 Un par de módulos de radio NRF2401,

conexiones

 Algunos cables de protoboard, preferiblemente Dupont macho/hembra más una protoboard

 

Las conexiones de radio

 

En la sesión anterior establecimos una comunicación RF muy básica entre un emisor y un receptor de radio controlado con nuestros Arduinos.

Era un ejemplo muy sencillo, de hecho, procuramos que fuera la mínima expresión para facilitar la comprensión del ejemplo y para combatir el miedo irracional que las comunicaciones de radio parece producir en muchos aficionados competentes.

Nunca entendí porqué. Parece que esa aura fantasmal que impregnaba las comunicaciones de radiofrecuencia a principios del siglo XX sigue vivo en el XXI.

Pero en realidad, gracias a ingenieros anónimos, como los que nos ofrecen las NRF24L01, son muy fáciles de utilizar sin más que un pequeño empujón de ayuda, algo que por cierto estaría bien que nos dieran los ejemplos que vienen con la librería (Que hace falta ser egiptólogo para descifrarlos)

Y es una pena, porque estos modulitos cumplen la regla de las 3 Bs, Bueno, bonito y Barato y esto no es frecuente.

Por eso vamos a seguir incluyendo tutoriales de uso de estos módulos RF, y como no podía ser de otra manera, teníamos que ampliar el tutorial a un ejemplo que hiciera comunicación bidireccional (Dúplex) de modo que un emisor envíe una secuencia de datos, que se reciben en un segundo nodo y nos responde para saber que ha llegado el mensaje.

Pero antes de empezar con el ejemplo tenéis que saber que el módulo NRF24L01 es un transceptor y no un emisor receptor.

Un equipo emisor receptor puede enviar mensajes de radio y recibirlos simultáneamente porque ambos circuitos, aunque muy similares, están aislados uno del otro y pueden operarse independientemente.

Estos equipos se venden y están disponibles en el mercado, ¿Pero sabéis que? Cuestan pasta. Por eso cuando alguien quiere ahorrar unos euritos en la radio y dado que el emisor y el receptor son bastante similares, se monta un circuito con partes comunes. De este modo podemos emitir o recibir (Observad el uso de la disyuntiva no copulativa) pero, y este es el truco, no a la vez.

 
  • No confundir con transponder o tranpondedor que es una cosa diferente. Aunque también es un sistema de radio, el transponder emite una señal y espera una respuesta, que dependiendo de la que sea puede poner o no en marcha una serie de acciones automáticas, como abrir la puerta de tu coche, o reclamar la identificar un avión en vuelo con su posición.
 

Por eso nuestros módulos NRF2401, pueden estar en modo recepción o en modo emisión, pero no en ambos. Aunque eso sí, podemos cambiar de uno a otro a voluntad, muy al estilo de las radios de aficionados que indican con un “corto” la intención de cambiar de modo.

 

Las instrucciones básicas

 

Aunque ya vimos algunas en la pasada sesión, vamos a insistir con ellas. Lo primero es incluir algunas librerías:

#include <SPI.h>
#include "nRF24L01.h"
#include "RF24.h"

Y después inicializamos los pines de control del módulo (Además, claro, de los de control SPI, de los que se encarga la librería):

RF24 radio(9,10);

En la última sesión dijimos que los pipes se pueden entender más o menos como canales de comunicación que no se interfieren unos con otros y definimos uno para enviar nuestros mensajes.

Como lo que queremos ahora es generar una comunicación en ambos sentidos, vamos a necesitar dos canales o pipes, por el primero hablará uno y escuchará el otro, y por el segundo funcionará al revés. Pero recordad que no podemos hacerlo a la vez.

const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL };

Son un par de enteros de 64 bits y no os preocupéis mucho por ellos. Para iniciar la radio hacemos:

  radio.begin();

Normalmente cuando envías un mensaje por radio, uno espera que llegue bien a la primera, pero la realidad suele tener su propia opinión y hacer de las suyas. Por eso es buena idea que si no recibimos un mensaje correctamente, se reenvíe para garantizar la entrega.

 
  • Hay una relación muy estrecha entre las señales de radio frecuencia, la electricidad y el electromagnetismo y generalmente cuando se produce una, también aparecen las otras.
  • Hay cantidad de fenómenos naturales que generan señales de RF espurias, como tormentas y rayos o tormentas solares incidiendo en las capas superiores de la atmosfera, pero también se generan por la actividades humanas tales como arrancar cualquier cosa eléctrica, como motores o lámparas fluorescentes.
  • Hay todo un muestrario de fenómenos que te producirán interferencias RF, antes o después y es mejor prevenirlo.
 

El problema es cuantas veces hacemos este reenvío y cada cuanto tiempo. Por ese tenemos una instrucción como esta:

radio.setRetries(15,15);

Fija en el primer parámetro la pausa entre reintentos, en múltiplos de 250µS, por eso 0 son 250µS  y 15 son  400µS.(La radio funciona a velocidad de vértigo)

El segundo parámetro indica cuantos reintentos se deben hacer hasta dejarlo, con un máximo de 15.

 
  • Si bajamos estos valores, la velocidad de comunicación sube, pero también los errores y pueden perderse datos. Por eso si la velocidad no es una cuestión decisiva (Y con Arduino rara vez lo será) es preferible dejarlo en 15,15 y listo.
 

También te interesa saber que independientemente de la longitud del mensaje que tú quieras enviar, el módulo lo partirá en paquetes de una longitud dada. Si no especificas un tamaño, el modulo usara 32 Bytes.

A esta longitud se le llama carga útil del paquete o PayLoad, y en caso de que haya un error en cualquiera de los bits de la transmisión, todo el paquete debe ser reenviado de nuevo (Pero no te preocupes que el NRF2401 lo gestione en automático por ti).

setPayloadSize (número de Bytes)

 
  • Si tienes muchos errores de transmisión, puedes bajar este número para evitar reintentos, pero ten en cuenta que el paquete conlleva una carga propia entre las radios y psi lo bajas mucho, puedes estar enviando muchos más datos de control que de mensajes y esto es un desperdicio.
 

Por ultimo tendremos que abrir los canales de comunicación o pipes, uno para emitir y el  otro para recibir

radio.openWritingPipe(pipes[0]);
radio.openReadingPipe(1,pipes[1]);

Y a partir de ahora, ya podemos empezar a escribir lineas de código.

 

Prgramando la comunicación de un par de módulos NRF2401

 

Montaremos de nuevo un par de Arduinos conectados mediante los NRF2401, con el mismo esquema que en la sesión anterior y que repito aquí para vagos y gente con muchas cosas que hacer:

Diagrama de protoboard

De nuevo tendremos dos roles diferenciados. Uno será el emisor y el otro el receptor. Comencemos con el programa del emisor Prog_79B_Emisor:

#include <SPI.h>
#include "nRF24L01.h"
#include "RF24.h"

RF24 radio(9,10);
const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL };

void setup(void)
   { pinMode(10, OUTPUT);
     Serial.begin(9600);
     radio.begin();

     radio.setRetries(15,15);      // Maximos reintentos
     //radio.setPayloadSize(8);    // Reduce el payload de 32 si tienes problemas
     radio.openWritingPipe(pipes[0]);
     radio.openReadingPipe(1,pipes[1]);
   }

Nada nuevo en el setup. Veamos el loop:

void loop(void)
   { 
      radio.stopListening();          // Paramos la escucha para poder hablar
      unsigned long time = millis();
      Serial.print("Enviando  ") ;  Serial.println(time) ;
      bool ok = radio.write( &time, sizeof(unsigned long) );

      if (ok)
           Serial.println("ok...");
      else
           Serial.println("failed");
      
      radio.startListening();                      //Volvemos a la escucha

Dejamos de escuchar en la radio para poder emitir. Leemos el valor de millis (O de cualquier otro parámetro que queramos enviar, como una temperatura)  y lo enviamos para después testear si la emisión ha sido o no correcta.

unsigned long started_waiting_at = millis();
bool timeout = false;
while ( ! radio.available() && ! timeout )       // Esperamos 200ms
        if (millis() - started_waiting_at > 200 )
            timeout = true;

Esperamos la confirmación de que han recibido el mensaje en destino, durante 200 ms. Y al salir comprobamos si se acabó el tiempo, o hemos recibido un mensaje.

Si ha disparado el timeout imprimimos un mensaje de error, y en caso contrario procesamos el mensaje recibido.

if ( timeout )
       Serial.println("Error, No ha habido respuesta a tiempo");
else
   {   // Leemos el mensaje recibido
       unsigned long got_time;
       radio.read( &got_time, sizeof(unsigned long) );

       Serial.print("Respuesta = ");
       Serial.println(got_time);
   }
delay(500);

Creo que no tendréis problemas con este programa. Vamos con el receptor Prog_79_B_receptor.

El setup es exactamente igual que en el emisor con una pequeña diferencia clave, los pipes de comunicación se invierten, ya que si el emisor escucha por un canal, el receptor necesariamente tiene que hablar por él y viceversa, para que se puedan poner de acuerdo.

Por eso, el receptor empieza a escuchar en el setup y define los pipes de lectura y escritura invertidos con relación al emisor:

radio.begin();
radio.startListening();
radio.openWritingPipe(pipes[1]);
radio.openReadingPipe(1,pipes[0]);

Y para el loop del receptor tenemos:

if ( radio.available() )      // Si hay datos disponibles
   { 
       unsigned long got_time;
       bool done = false;
       while (!done)          // Espera aqui hasta recibir algo
          {
              done = radio.read( &got_time, sizeof(unsigned long) );
              Serial.print("Dato Recibido =");
              Serial.println(got_time);
              delay(20);             // Para dar tiempo al emisor
          }
       radio.stopListening();        // Dejamos d escuchar para poder hablar

       radio.write( &got_time, sizeof(unsigned long) );
       Serial.println("Enviando Respuesta");

       // Volvemos a la escucha para recibir mas paquetes
       radio.startListening();
   }

Aquí os pongo un pequeño video mostrando el resultado de la comunicación:

 

He hecho una prueba usando estos programas dejando el emisor en mi PC y colocando el receptor en mi portátil, para ver moviéndome por casa, hasta donde me he podido alejar sin perder la señal.

El resultado ha sido bastante pobre, ya que en cuanto colocas paredes de por medio la comunicaron se pierde con rapidez a partir de 10 metros, lo que no resulta extraño dado que estoy usando módulos con antena integrada.

Normalmente a estos módulos se les asigna un alcance de 20 o 25 m sin obstáculos y esta distancia disminuye rápidamente a medidas que la señal encuentra obstáculos, ya que la potencia de emisión es muy baja y por tanto, el resultado de la prueba concuerda con esta idea.

Si queremos ampliar el radio de cobertura tendremos que pasar a usar antenas amplificadas, similares a estas:

Muestra antena y modulo separados
Modulo RF con antena

Estos módulos incluyen, no solo un adaptador de antena, además llevan integrado un amplificador de señal para potenciar la emisión y recepción de señal, con lo que la distancia útil de transmisión crece hasta unos 100 metros (Según he visto en Internet).

En las pruebas que hemos hecho nosotros usando un par de estos módulos, puedes conectar cualquier punto de una casa de tamaño medio sin problemas, pero no los hemos podido probar en el campo.

Por último y para cerrar esta sesión, comentar  que también existe disponibles adaptadores USB con NRF2401 incluido, lo que nos permite enviar y recibir mensajes desde nuestro PC a estos módulos.

De hecho tengo uno de un fabricante chino, pero me temo que Windows se niega tercamente a reconocerlo y la página del fabricante es un desastre en el que soy incapaz de encontrar ningún driver para él.

Pero vale la pena comentar, que disponer de un medio de interactuar con estas radios desde nuestros portátiles es una utilidad que amplia enormemente las posibilidades de comunicación y control de nuestros Arduinos.

Seguiré insistiendo con él y si consigo arrancarlo, os lo hare saber.

 

Resumen de la sesión

 

 
    • Seguimos profundizando en los módulos NRF24L01.
    • Hemos visto más comandos disponibles en la librería que nos ayudaran a minimizar los problemas de interferencias y reenvíos.
    • Escribimos unos programa básicos para comunicar dos de estos módulos y establecer un canal de ida y vuelta