Objetivos
Material requerido.
Arduino Uno o similar. | |
Modulo NRF2401 | |
Cables Dupont Macho-Hembra |
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:
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
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:
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
>