24 marzo 2014

Arduino y el XBee Series 1 - (Modo API)

En una entrada previa, les mostraba un muy corto video donde explicaba como configurar una XBee Series1 (XBee S1) en modo transparente. Este modo de configuración permite crear un enlace entre dos XBee muy rápidamente y prácticamente sin mayor configuración

Sin embargo, cuando queremos armar una red inalámbrica donde necesitamos que se comuniquen entre si varios dispositivos o simplemente queremos conocer algo tan sencillo como la dirección del dispositivo que nos está enviando datos, el modo transparente resulta sumamente limitado.

En esta entrada explicaremos un poco el funcionamiento de las redes con XBee y además les compartiremos un código muy fácil de usar y entender para utilizar las XBee Series
1 en modo API.

La entrada es un poco extensa pero para que se hagan una mejor idea de lo que se puede lograr con el modo API les dejo un corto video:




¿Qué son y para que sirven las XBee?


XBee es el nombre comercial de un conjunto de soluciones de conexión inalámbrica fabricados por Digi International. Esta empresa se dedica a fabricar módulos que requieren poca o nula configuración para ponerlos a trabajar.

Esto hace estos módulos ideales para cuando nos interesa desarrollar un prototipo rápido sin tener que meternos demasiado las manos en el mundo de la radio-frecuencia que de por sí es bastante complejo.

Los módulos también poseen una característica muy apreciada y es que poseen un conector "estándar" que no requiere de soldadura, esto resulta muy práctico, cuando por ejemplo estamos desarrollando un prototipo con un módulo XBee de corto rango y luego queremos migrar a uno de mayor alcance.

El modelo en particular que vamos a revisar en esta entrada es el XBee S1, uno de los modelos de más bajo costo, más fácil de usar y configurar de los módulos XBee.

La lógica de funcionamiento de estos módulos es sumamente sencilla, tienen un puerto Serie que conectamos a nuestro Arduino y todo lo que escribamos al puerto serie se enviará al módulo o módulos que hayamos configurado como destino.

Hablando un poco de Redes Malladas


Red MalladaTal vez la característica más interesante de los módulos XBee S1 es que son capaces de establecer conexiones de redes tipo "malla", en este tipo de red no existe un nodo central que se encargue de pasar los mensajes sino que cada nodo en la red está conectado a todos los nodos cercanos y los mensajes se transmiten saltando de un nodo a otro.

Es posible sin embargo, configurar los módulos de tal manera que dependan de un nodo "coordinador". Esta opción requiere de más configuración pero permite acceder a otras funciones no disponibles en la configuración por defecto como poner a dormir(modo de bajo consumo) a los nodos de manera sincronizada o de-sincronizada y la posibilidad de re-transmitir mensajes a nodos que por A o B motivo se desconectan de la red o no son accesibles temporalmente (transmisión indirecta).

Vamos a centrarnos por el momento en las redes malladas ya que es la configuración por defecto que trae el XBee y para la mayoría de aplicaciones esto será más que suficiente.

Direcciones de Hardware


Para comunicarnos entre dos XBee dentro de la red mallada necesitamos conocer la dirección del XBee destino al cual queremos enviar los datos. Cada XBee S1 posee una dirección única de hardware asignada por la IEEE, de esta manera no hay dos módulos XBee que con la misma dirección.

Para conocer la dirección de tu XBee solo tienes que revisar la parte inferior del módulo y notarás un número de serie con 16 dígitos hexadecimales. Este número corresponde a la dirección de hardware. Esta dirección es fija y viene grabada de manera permanente en el firmware del XBee así que no es posible modificarla.

También, de manera opcional, podemos identificar los dispositivos a través de una dirección corta de 16 bits. En la mayoría de aplicaciones se esperaría que no que necesites tener más de 65,535 nodos en tu red. A diferencia de la dirección de hardware, es posible modificar esta dirección "corta". Por defecto, se utilizan la dirección 0x00 como dirección corta. Podemos configurar esta dirección a través del parámetro es "MY" en el modo de comandos.

Es posible además de la dirección corta, especificar una dirección para la red de área personal (PAN) con el parámetro "ID" y un canal de comunicaciones con el parámetro "CH". Con estos dos parámetros es posible segmentar y crear sub-redes ya que solo los XBee que compartan el mismo ID de PAN y el mismo canal se comunicarán entre si.

La dirección de Broadcast


También existe una dirección especial de 16bits llamada de "broadcast". Esta dirección, por convención, se deberá utilizar cuando queramos que todos los nodos en una red de área personal (PAN por sus siglas en ingles) reciban el mensaje que deseamos transmitir. Para enviar un broadcast a través del XBee debes establecer la dirección de destino con el valor 0xFFFF.

Solo asegúrate de tomar algo en consideración: Debes de ser muy cuidadoso con los mensajes de broadcast ya que estos se intentan replicar a todos los nodos en la red. Si uno o varios nodos están enviando demasiados mensajes de broadcast se corre el riesgo de saturar la red y dificultar el envío de datos. 

Modo de funcionamiento


El XBee interactúa con el mundo externo esencialmente en tres modos:
  • El modo transparente: Que es el equivalente a un enlace serie virtual entre dos módulos XBee en base a su configuración.
  • Los modos API 1 y 2: Que permiten enviar y recibir datos a través del envío de "paquetes" a través de estructuras de datos predefinidas.
  • El modo de comandos: Utilizado para cambiar los parámetros de configuración del XBee.
Vale la pena recordar que el modo transparente es el modo más sencillo de uso, pero al mismo tiempo hay algunas operaciones que son difíciles, si no imposibles, de realizar en modo transparente como por ejemplo:
  • Transmitir datos a todos los nodos en la red (broadcast).
  • Cambiar de módulo destino con cada transmisión.
  • Obtener la dirección del módulo que origina del dato recibido.
  • Cambiar parámetros de configuración "al vuelo".
  • Modificar remotamente la configuración de un módulo XBee.
Para entender lo anterior de mejor manera vamos a revisar los distintos modos de funcionamiento.

Modo de Comandos


El modo de comandos es la forma en que podemos configurar el módulo XBee desde el propio puerto Serial. La configuración se realiza a través del envío de los "comandos AT", estos no son más que palabras clave precedidas de las letras "AT" (de ahí el nombre).

La herramienta de escritorio X-CTU no es más que una interfaz gráfica que nos permite modificar el conjunto de parámetros de nuestro XBee de una manera amigable y sencilla.

Podemos solventar algunos de los problemas mencionados en el punto anterior cambiando de forma creativa entre modo transparente y modo de comandos.

Si por ejemplo quisiéramos cambiar el destinatario de los datos que enviaremos a través del XBee deberíamos seguir la siguiente secuencia antes de iniciar la transmisión (Entre paréntesis los comandos AT).

  1. Entrar al modo comando (+++) y esperar por "OK".
  2. Cambiar la dirección destino y esperar "OK".
    1. Usando direccion de 64 bits: (ATDHXXXXXXXX, ATDLXXXXXXXX), donde DH  y DL son los bits más y menos significativos de la dirección de hardware respectivamente, colocar MY en 0xFFFF (ATMYFFFF).
    2. Usando dirección de 16 bits: (ATDH0, ATDLXXXX), donde DL son los 16 bits de la dirección corta a la que nos deseamos conectar, colocar MY con la dirección que deseamos asignar al módulo local (ATMYXXXX).
  3. Salir del modo comando (ATCN) y esperar por "OK"
  4. Transmitir nuestros datos a través de DIN.

Nota: Para la dirección de 16 bits, asumimos que el módulo se encuentra en configuración de fábrica con los parámetros CE = "0" (Habilitar coordinador), A1= "0" y que todos los módulos tienen los mismos valores en los parámetros ID (Id de la PAN) y CH (Canal de operación).

De este modo cada vez que queramos enviar datos debemos realizar esta secuencia de comandos. Posiblemente para fines educativos resulte interesante, pero es también lento y requiere que estemos monitoreando continuamente el puerto serie para verificar que los comandos se hayan ejecutado satisfactoriamente y en el orden que especificamos.

Modo API


El modo API ciertamente es más complicado a nivel de programación pero provee de mayor "flexibilidad" al momento de realizar el envío y recepción de datos.

A diferencia del modo transparente, el modo API hace que el módulo XBee espere por una secuencia específica de bytes que le indican que tipo de operación deberá de realizar. Cada uno de estas secuencias se le conoce como "frame" o también le podemos llamar paquete. Y dependiendo del contenido de cada paquete se llamará a una función u otra de la API.

De igual manera, el módulo XBee responde con secuencias específicas dependiendo si lo que está reportando es un cambio en el estado del módem, en el estado de la transmisión o si está recibiendo un paquete de datos desde un nodo remoto.

Cada paquete presenta la siguiente estructura:



Donde cada parte corresponde a:
  1. Byte 1: El encabezado que siempre tiene un valor de 0x7E, esto indica al módulo que está por comenzar la transmisión de un paquete.
  2. Bytes 2, 3: Esta sección indica el tamaño del paquete, el tamaño se calcula contando los bits entre el byte menos significativo del tamaño y el byte de la suma de verificación (checksum).
  3. Bytes 4-n: Esta sección contiene la estructura especifica a la API, de los contenidos de esta parte del paquete dependerá la función de la API que se llame.
  4. Byte n+1: La suma de verificación de todos los bytes contenidos en el paquete. Esto se utiliza para verificar la "integridad" de los datos recibidos, si la suma no coincide el paquete es descartado.
El contenido del paquete de datos varía  en base al comando que se quiera transmitir, por ejemplo si quisiéramos transmitir un dato a otro XBee utilizando una dirección de 16 bits deberíamos utilizar la siguiente estructura:


Donde la estructura tiene la siguiente forma:

  1. Byte 4: Identificador de la API, este código identifica el tipo de comando que queremos ejecutar, para este caso el código 0x01 corresponde a "Transmit Request: 16-Bit Address" o solicitud de transmisión a una dirección de 16 bits.
  2. Byte 5: Corresponde a un numero secuencial del paquete. Para cada envío se recibe un "acuse de recibo" (ACK) este número nos servirá luego para verificar que un paquete que enviamos fue recibido por el destinatario.
  3. Bytes 6, 7: Corresponden a la dirección del XBee destinatario, se coloca primero el byte más significativo y luego el menos significativo.
  4. Byte 8: Corresponde a las opciones de envío, si establecemos el bit 0x01 en 1 no se esperará un ACK, mientras que si habilitamos el bit 0x04 se enviará el mensaje con broadcast para todas las redes de área personal (PAN).
  5. Byte 9-n: Datos a enviar (máximo 100 bytes).

El modo API 1 y API 2


Según la documentación del XBee existen dos modos API, la diferencia entre el modo 1 y el modo 2 es que en el Modo 2 las secuencias de bytes van "escapadas".

¿Qué significa esto? Resulta que el módulo XBee intentará leer el inicio de un paquete siempre encuentre el byte 0x7E, ¿pero qué pasa si este valor se está enviando en algún lugar dentro del paquete? Por ejemplo en el tamaño, o en la misma secuencia de datos.

Pues lo que ocurrirá será que el XBee va a descartar el paquete que estaba procesando y comenzará a procesar el nuevo de manera errónea. Para solventar este problema se incluyó el Modo API 2, en este modo antes de enviar un dato verificamos si el byte posee cualquiera de los siguientes valores:
  • 0x7E: Inicio del paquete.
  • 0x7D: Carácter de escape.
  • 0x11: Señalizador XON
  • 0x13: Señalizador XOFF
Para escapar la secuencia tenemos que enviar el carácter de escape más el dato que queremos enviar haciendo una operación XOR con el valor 0x20.

Por ejemplo, la secuencia del siguiente paquete:

0x7E 0x00 0x02 0x23 0x11 0xCB

Quedaría escapado de la siguiente manera:

0x7E 0x00 0x02 0x23 0x7D 0x31 0xCB

Entrando al Modo API

Para habilitar el modo API debemos enviar la siguiente secuencia de comandos a nuestra XBee:

Modo API 1:
  • Enviamos "+++" y esperamos "OK".
  • Enviamos "ATAP1" y esperamos "OK"
  • Enviamos "ATCN" y esperamos "OK"
Modo API 2:
  • Enviamos "+++" y esperamos "OK".
  • Enviamos "ATAP2" y esperamos "OK"
  • Enviamos "ATCN" y esperamos "OK"
Una vez el XBee se encuentre en modo API, ignorará todos los datos que no muestren la secuencia correspondiente a una estructura de la API. Esto resulta muy útil ya que podemos imprimir sobre la misma línea Serie donde está conectado el XBee mensajes para "depurar" el funcionamiento y el XBee simplemente los ignorará.

Tomen en consideración que los datos recibidos del XBee también presentarán la estructura de la API así que leer los datos requerirá realizar el proceso detallado anteriormente pero a la inversa.

mensaje Un pequeño ejemplo...


Yo se que hasta aquí todo se ha vuelto sumamente confuso, pero vamos a dar un ejemplo, si quisiéramos enviar el texto "Hello World!" en un broadcast, deberíamos enviar la siguiente secuencia de bytes al XBee:

0x7E: Encabezado
0x00:
0x11: Tamaño en Bytes del paquete (17)
0x01: Identificación del comando de la API (0x01 para enviar a dirección de 16 bits)
0x01: Identificación del paquete (cualquier numero entre 0x01 y 0xFF)
0xFF: Byte más significativo dirección destino
0xFF: Byte menos significativo dirección destino
0x00: Opciones, "0" utiliza opciones por defecto.
0x48: 'H'
0x65: 'e'
0x6C: 'l'
0x6C: 'l'
0x6F: 'o'
0x20: ' '
0x57: 'W'
0x6F: 'o'
0x72: 'r'
0x6C: 'l'
0x64: 'd'
0x21: '!'
0xC3: Suma de verificación

La suma de verificación


El último byte, la suma de verificación,  se utiliza para garantizar que el paquete se haya transmitido de manera integra, lo calculamos restando a 0xFF la suma truncada a 8 bits de  todos los bytes entre el tamaño del paquete y el checksum, para el ejemplo anterior la fórmula del checksum sería:

0xFF- 0xFF&(0x01+ 0xFF + 0xFF + 0x00 +0x48 +
            0x65 + 0x6C + 0x6C +0x6F + 0x20 +
            0x57 + 0x6F +0x72 +0x6C + 0x64 +0x21) = 0xC3


Comandos disponibles en la API


En esta entrada no voy a explicar a detalle comando por comando, puedes encontrar muy detallada en el manual del XBee, sin embargo vale la pena mencionarlos rápidamente y explicar que hacen, coloco entre "[]" si son comandos de entrada (que se envían al modem) o salida (que se reciben del modem) y entre "()" el identificador del comando en la API:
  • Modem Status (0x8A) [salida]: Es generado cuando el estado del modem cambia, por ejemplo cuando se resetea, cuando se asocia a una PAN o cuando pierde la conexión.
  • AT Command (0x08) [entrada]: Es el equivalente de la "consola" pero sin necesidad de entrar al modo de consola de comandos. Nos permite cambiar parámetros de configuración al vuelo.
  • AT Command - Queue Parameter Value (0x09) [entrada]: Al igual que el anterior nos permite enviar comandos AT pero no son aplicados a la configuración de manera inmediata sino hasta que enviamos un comando AT(0x08) o hasta que enviamos el comando ATAC (Aplicar cambios).
  • AT Command Response(0x88) [salida]: Este comando lo genera el modem luego de que ha  recibdo un comando AT o luego de que se han aplicado los cambios. Esencialmente podemos saber si el comando fué aceptado o nos muestra los valores que estamos consultando.
  • Remote AT Command Request (0x17) [entrada]: Usaremos este comando cuando necesitamos configurar un modem remoto. Los XBee soportan reconfiguración "sobre el aire" así que si cambiamos la configuración de la red podemos acceder a los modems remotos para que se reflejen los cambios.
  • Remote Command Response (0x97) [salida]: Al igual que el AT Command Response, el modem genera este paquete cada vez que se confirma la aplicación de una configuración en un modem remoto.
  • TX (Transmit) Request: 64-bit address [entrada]: Utilizaremos este comando cuando querramos transmitir a una dirección de 64 bits. El formato es muy similar al comando que se explico en el ejemplo con la diferencia de que se utilizan más bytes para definir la dirección destino.
  • TX (Transmit) Request: 16-bit address [entrada]: Utilizaremos este comando cuando querramos transmitir a una dirección corta de 16 bits. Este es el comando que explicamos en el punto anterior.
  • TX (Transmit) Status [salida]: El modem genera este paquete cuando confirma que ha enviado o se ha cancelado el envío de un paquete, resulta muy útil para saber si el mensaje fué enviado al nodo destinatario.
  • RX (Receive) Packet: 64-bit Address [salida]: El modem genera este paquete cada vez que recibe datos hacia su dirección de 64 bits, es posible saber la dirección de origen, la intensidad de la señal y las opciones con que fueron enviado los datos.
  • RX (Receive) Packet: 16-bit Address [salida]: Al igual que el anterior este se genera cada vez que se reciben datos en la dirección corta de 16 bits, de igual manera es posible saber la dirección origen, intensidad de señal y opciones con que fué enviado el paquete.

Las buenas noticias son...


Para este ejemplo no vamos a tener que implementar todo esto que he escrito por nosotros mismos. La biblioteca TeUbico_XBee incluye una pequeña clase que implementa todo el código encargado de configurar y poner a trabajar nuestro XBee en modo API y nos facilita algunas funciones para el envío y recepción de datos a direcciones de 64 y 16 bits.

¿Por qué utilizar esta biblioteca y no otra pre-armada?


  1. Funciona de manera nativa en modo API-2, todo lo que he escrito arriba está implementado en esta biblioteca tanto para envío como para recepción de datos.
  2. Está programada "basada en eventos", de esta manera no tenemos que enviar una secuencia y esperar la respuesta, sino que enviamos el mensaje y la biblioteca se encargará de llamar a la función que le indiquemos una vez se reciba una respuesta.
  3. Es una implementación "incompleta" pero fácilmente expansible. Decidí en vez de implementar toda la funcionalidad de la XBee Serie 1 en sofware hacer una implementación parcial pero que dejara acceso a la lectura directa de los paquetes recibidos así se puede ampliar y agregar funcionalidad avanzada muy fácilmente.
  4. No bloquea el loop principal, de esta manera no es necesario que "esperemos" continuamente a que la biblioteca nos de una respuesta permitiendo que se puedan hacer programas que funcionen de manera asínctrona.  
La biblioteca funciona "pegándose" a un puerto serial de hardware y se encarga de poner el XBee en modo API y de mantenerse a la escucha de nuevos paquetes.

Para utilizarla lo primero que tenemos que hacer es descargar la biblioteca de la siguiente dirección:


Descomprimelo y copia la carpeta TeUbico_Xbee en el directorio libraries de tu instalación de Arduino.

Conectando Nuestro Arduino


Esta parte es sumamente sencilla, necesitaremos los siguientes materiales:
  1. Arduino UNO/Mega/Leonardo
  2. XBee Explorer Regulated
  3. XBee Series 1
  4. Cables Jumper
  5. (Opcional): Mini Breadboard 
Conectamos el XBee Explorer Regulated y nuestro Arduino de la siguiente manera:
  • DOUT -> RX<-0
  • DIN <- TX->1
  • 5V <-> 5V
  • GND <-> GND

Nota: Si están utilizando el UNO, recuerden que el puerto Serial es compartido con la XBee así que recuerden desconectar las líneas TX y RX antes de cargar su sketch, si están utilizando el Leonardo, el puerto serie es Serial1, si utilizan el Mega pueden utilizar el Serial1, Serial2 o Serial3 para que no tengan conflictos a la hora de cargar su sketch.

Inicializando nuestro XBee 


  1. Copiamos la carpeta TeUbico_XBee a nuestra carpeta "libraries" de Arduino y creamos un nuevo Sketch.
  2. Incluímos los siguientes archivos de encabezados:
    #include "teubico_XBeeS1.h"
    #include "teubico_utils.h"
  3. Creamos una nueva variable XBeeS1 de la siguiente forma (procura no olvidar el *):
    XBeeS1 * myXBee;
  4. En el setup, es necesario que definamos que puerto serial de hardware utilizaremos y la dirección del módulo local:
    myXBee = new XBeeS1(Serial,0x0001);
  5. Luego llamamos a "init" desde el setup, este se encarga de colocar el XBee en modo API.
    myXBee->init();
  6. Por último dentro de loop() debemos incluir el comando siguiente:
    myXBee->listen();

    Esto último se encargará de que se escuche continuamente el puerto serie por paquetes nuevos.

El código debería quedar más o menos de la siguiente manera:

#include <teubico_XBeeS1.h>
#include <teubico_utils.h>

XBeeS1 * myXBee;

void setup() {
  
  Serial.begin(9600);
  myXBee = new XBeeS1(&Serial);
  myXBee->init();
  
}

void loop() {
  // Listen for new frames
  myXBee->listen();
}

Enviando datos a la red


Hasta aquí todo muy bonito pero nuestro código únicamente coloca al XBee en modo API pero no procesa los datos de ninguna manera. Vamos a agregar ahora el código para enviar datos a nuestra red.

Esto es sumamente sumamente sencillo, la biblioteca tiene dos funciones "sendTo16" y "sendTo64", por ejemplo para enviar un broadcast podemos hacerlo de cualquiera de las siguientes maneras:

// Enviando a una dirección de 64 bits
myXBee->sendTo64(0x8839123,0x3324A4DE,"Hello World\r\n");

// Enviando a una dirección de 16 bits
myXBee->sendTo64(0xA4DE,"Hello World\r\n");


Podemos cambiar los parámetros para que reflejen la dirección de un XBee en específico (usando la dirección del que se mostró en la foto):

// Enviando a una dirección de 64 bits
myXBee->sendTo64(0x0013A200,0x40A6C58A,"Hello World\r\n");


Para ahorrarnos código en este caso se utilizan los valores de configuración que estén almacenados en el XBee. No es necesario especificar una dirección ya que se utiliza siempre 0xFFFF como destino.

// Enviando un broadcast:
myXBee->broadcast("Hello World\r\n");


Nota importante: Minimizar el uso de delay()


Esta biblioteca funciona bajo la suposición que las demás instrucciones dentro de loop no "acaparan" tiempo de procesamiento.

Por ejemplo, el siguiente código no permitiría que el código XBeeS1 funcione de manera eficiente ya que el loop principal se mantendría la mayor parte del tiempo esperando a que termine el "delay" en vez de estar escuchando el puerto serie:

void loop() {
  myXBee->listen();

  delay(1000); // Retardo innecesario
  myXBee->sendTo64(0x00,0xFFFF,"Hello World\r\n");
}

La biblioteca teubico_utils.h incluye una función para chequear intervalos en Arduino muy fácilmente. podemos hacer algo similar a lo anterior utilizando un contador de tiempo que se activará aproximadamente cada segundo:
// Variable para el pseudo-timer
long lastMessage = 0;
void loop() {
  myXBee->listen();
  

  if(checkTimeout(lastMessage,1000)) {
    myXBee->sendTo64(0x00,0xFFFF,"Hello World\r\n");
    lastMessage = millis();
  }
}


Lo que hace checkTimeout es revisar la última vez que se reinicio el contador (en este caso llamado lastMessage), si supera el tiempo especificado en milisegundos devuelve "true".

Este último código es preferible al anterior porque no bloquea el bucle de ejecución principal.

Recibiendo datos


La parte de recepción de datos siempre resulta un poco compleja, más que todo porque implica estar monitoreando el puerto serial por el paquete correspondiente y procesarlo.

La biblioteca está basada en la filosofía de la programación basada en "eventos", lo que hace es escuchar continuamente el puerto serial y cuando recibe un paquete llama a un "manejador" (handler) que se encarga de reaccionar de manera acorde.

Los handlers disponibles en la biblioteca se manejan en el siguiente orden:
  • onFrameReceived: Permite especificar una función que se encargará de procesar el paquete recibido. Este recibe el paquete completo, esta función te sirve por ejemplo si estás trabajando con una versión más nueva de la API que implemente otro tipo de respuestas.
  • onTXStatus: Permite especificar la función que se llamará al momento de recibir un paquete de TX Status, muy útil para verificar si nuestros datos fueron enviados al destinatario.
  • onReceiveData64: Permite especificar la función que se llamará al momento de recibir un paquete a la dirección de hardware de 64 bits, es posible conocer la dirección origen del paquete. Esta se llama únicamente cuando hemos establecido la dirección del XBee a "0xFFFF".
  • onReceiveData16: Permite especificar la función que se llamará al momento de recibir un paquete a la dirección de hardware de 16 bits, al igual que el anterior podemos conocer la dirección origen del paquete.
  • onReceiveData: Igual que los anteriores pero muestra únicamente los datos, esta se llama indiferentemente si los datos se originaron de una dirección de 64 bits o 16 bits. Es llamada justo despues de onReceiveData64 o onReceiveData16.

Podemos implementar uno, varios o todos de los handler de la siguiente manera:

Para procesar un dato recibido: 

La bibloteca provee de tres eventos para procesar datos, la más utilizada es la recepción de datos en modo 16 bits:
  1. En el setup, asignamos el handler correspondiente, por ejemplo TX Status:
    myXBee->onDataReceived16(dataReceived16);
  2. Y generamos el handler acorde:
    void dataReceived16(uint16_t addr, uint8_t *data, uint16_t dataSize) {
      Serial.print("Data Received: ");
      for(int i=0;i<dataSize;i++) {
        Serial.write(data[i]);
      }
    }
Para el caso de la recepción de datos en vez de devolvernos una cadena de caracteres, se nos devuelve un arreglo de "enteros". Esto funciona de esta manera porque realmente no sabemos que tipo de dato nos fue enviado. Por ello también tenemos una variable llamada "dataSize" que nos sirve para determinar el tamaño del arreglo recibido y procesarlo de manera acorde.

Para procesar el estado del envío del mensaje:

  1. En el setup, asignamos el handler correspondiente, por ejemplo TX Status:
    myXBee->onTXStatus(txStatusReceived);
  2. Luego creamos una función que se encargará de recibir los datos como la siguiente:
    void txStatusReceived(uint8_t seq, uint8_t statusCode) {
      Serial.print("Sequence number: ");
      Serial.println(seq, HEX);
      Serial.print("Status: ");
      switch(statusCode) {
        case 0:
          Serial.println("Success");
          break;
        case 1:
          Serial.println("No ACK (Acknowledgement) received");
          break;
        case 2:
          Serial.println("CCA failure");
          break;
        case 3:
          Serial.println("Purged");
          break;
      }
    }
  3. Esta función se encargará de imprimir el estado del envío del mensaje cada vez que hacemos un TX Request

Un ejemplo Completo



Este ejemplo es un poco más extenso pero demuestra toda la funcionalidad en la bibloteca. La idea es sencilla: tenemos tres XBee conectadas entre sí, dos de ellas tienen una dirección de 16 bits asignada, para el ejemplo las direcciónes 1 y 2. Cada vez que una de estas XBee recibe un dato, notifican a una tercera que recibieron el mensaje, para esta última el mensaje se envía utilizando una dirección de 64 bits. Esta última está conectada directamente a la PC y funciona en modo transparente.

#include <teubico_XBeeSeries1.h>
#include <teubico_utils.h>

XBeeSeries1 * myXBee;

// Variable para el pseudo-timer
long lastMessage = 0;

void setup() {
  
  Serial.begin(9600);
  // Indicamos que debera de pegarse al Serial
  // Con la direccion 0x0002
  myXBee = new XBeeSeries1(Serial, 0x0002);
  myXBee->onTXStatus(statusReceived);
  myXBee->onDataReceived16(dataReceived);
  myXBee->init();
}

void loop() {
  // Escuchar por nuevos datos
  myXBee->listen();
  
  // Enviar un mensaje cada segundo aproximadamente
  if(checkTimeout(lastMessage,1000)) {
    myXBee->sendTo16(0x0001,"Hola UNO!\r\n");
    lastMessage = millis();
  }
}

// Procesa los datos recibidos
void dataReceived(uint16_t addr, uint8_t *data, uint16_t dataSize) {
  Serial.println("Datos recibidos");
  Serial.print("Direccion: 0x");
  Serial.print(addr,HEX);
  Serial.print('\n');
  Serial.println("--------");
  for(int i=0;i<dataSize;i++) {
    Serial.write(data[i]);
  }
  Serial.println("--------");
  myXBee->sendTo64(0x0013A200,0x40A6C58A,"Datos recibidos en 2, reportando a base.\r\n");
}

void statusReceived(uint8_t seq, uint8_t statusCode) {
  // Procesa el mensaje de status
  Serial.print("Numero de secuencia: ");
  Serial.println(seq, HEX);
  Serial.print("Estado: ");
  
  // Procesa el mensaje
  switch(statusCode) {
    case TX_STAT_SUCCESS:
      Serial.println("Success");
      break;
    case TX_STAT_NOACK:
      Serial.println("No ACK (Acknowledgement) received");
      break;
    case TX_STAT_CCAFAIL:
      Serial.println("CCA failure");
      break;
    case TX_STAT_PURGED:
      Serial.println("Purged");
      break;
  }
}

Puedes descargar el código de la siguiente dirección:



Concluyendo


Si bien el modo API puede parecer complicado, permite un nivel de flexibilidad imposible de obtener con el modo transparente. Ahora será más fácil que puedas construir aplicaciones en donde puedas tener muchísimos nodos conectandose entre sí de manera fácil y rápida.

La biblioteca que hemos desarrollado aún es un poco limitada pero espero dentro de poco agregar la funcionalidad faltante como envío y recepción de comandos AT vía API y configuración remota de módulos XBee. Sin embargo para comunicación sencilla entre módulos XBee deberá de ser más que suficiente.

No habiendo nada más que decir por hoy...

¡Hasta la próxima!

Más Información...


8 comentarios:

carlos andres dijo...

Companero tu ayuda. Estoy tratando de crear una red mesh con XBEE S2, uno estara conectada con la pc este creo deberia estar en modo 'coordinador' y los otros le enviaran info a el.?No estoy seguro de la config q debo ponerle a cada xbee xq por ejemple modo coordinador tiene algunas opciones? ?Como compruebo q ya han establecido la red mallada? Tu ayuda XFA!!!!!!

Israel De la fuente dijo...
Este comentario ha sido eliminado por el autor.
Israel De la fuente dijo...

Hola, tengo una pregunta. ¿Se pueden controlar las salidas digitales y pwm de un Xbee S1 desde otro Xbee S1 que esta conectado a un arduino. Aclaro que el Xbee al que deseo manipular las salidas, NO ESTA CONECTADO A NINGUN MICROCONTROLADOR. Saldos

Jonathan KS dijo...

Saludos, talvez me puedas ayudar a cambiar el canal, e ingresado (+++) luego el comando (ATCH10) para cambiar al canal 15 pero no se cambia, no se si me falta algun comando mas..!
espero tu ayuda
estoy implementando una red con XBEE PRO2SB, pero necesito un repetidor ya que la distancia es extramadamente larga, y no se como configurar al xbee como repetidor
gracias por la ayuda

karoluz dijo...

Se puede utilizar esta libreria para un XBee Serie 2

Arnold Verdugo dijo...

como puedo realizar una red de 4 nodos anclas transmisores, y un nodo movil? receptor los 5 dispositivos con Aruidno UNO

Jaime Corchado dijo...

Hola buenas no consigo hacer funcionar, el envio de un buffer[3] de enteros. Como le meto por parámetros algo que no sea un char? y ademas no entiendo del todo el tema de los puertos seria. Cuando creas el objeto myxbee y le metes el serial el segundo parámetro es la direccion del xbee actual? o se refiere a otra cosa

doteross dijo...

Hola esta bueno tu post, me podrías decir si hay alguna librería para la Serie 2 o puedo utilizar la que posteaste sin problemas. Espero tu respuesta.