f6ripaphqf9h5io-medium

En esta serie de entradas veremos cómo utilizar el puerto serie del Arduino como medio de comunicaciones principal. Usaremos IceC e IDM para la integración, lo que nos permitirá «hablar» con cualquier dispositivo, no importa que sea Ethernet, XBee, BLE, WiFi, RS-485…

Introducción

Arduino tiene shields de muchos tipos. Y muchos de ellos están diseñados para dotar al Arduino de algún tipo de comunicación: XBee, WiFi, Bluetooth, etc. Sin embargo, cualquier Arduino tiene al menos una forma de comunicarse: el puerto serie (en la mayoría de ocasiones, mediante USB).

Si conectamos este puerto serie a un PC (o similar, como una Raspberry Pi), podremos interactuar directamente con nuestro Arduino, bien para programarlo o bien para enviar/recibir datos desde nuestro programa. Este mecanismo, aunque útil en muchas situaciones, es difícil de ampliar. Por ejemplo, si queremos enviar el valor de un sensor de temperatura a un display WiFi, tendremos que escribir un programa específico que lea el valor del sensor usando el puerto serie, y que lo reenvié a su destino, usando la interfaz WiFi.

IDM te proporciona los mecanismos necesarios para evitar tener que escribir ese software adaptador. Y ahora, gracias al endpoint serie (SerialEndpoint), es posible utilizar directamente tu Arduino sin necesidad de ningún shield de comunicaciones.

Requisitos

Esta entrada ha sido escrita contando con la siguiente configuración (que será lo que necesites si quieres reproducir lo aquí descrito):

Software:

  • GNU/Linux (se usó Ubuntu 16.04, de 64 bits, mediante vagrant)
  • ZeroC Ice 3.6.2 (instrucciones de instalación)
  • Python ZeroC Ice 3.6.2 (instrucciones de instalación, comprueba que tienes instalados libssl-devlibbz2-dev)
  • IceC para Arduino (en su versión 20160927-1)
  • Endpoint Serie para ZeroC Ice 3.6.2 (en su versión 0.20161011-1); este endpoint se usará en la parte que se ejecuta en el PC
  • Endpoint Serie para Arco IceC (en su versión 0.20161013-1); este endpoint se emplea dentro de Arduino.
  • Arduino IDE 1.6.12 (descarga desde la web de arduino.cc)

Hardware:

  • PC de 64 bits, con USB
  • Arduino UNO (o compatible)

Por otro lado, se asume que el lector tiene ciertos conocimientos de programación en C/C++ y Python, que se siente cómodo con la consola y los entornos GNU/Linux, y que tiene alguna experiencia con Arduino, y los sistemas distribuidos (en particular con ZeroC Ice).

Instalación

NOTA: Se asume que tienes instalado el sistema correctamente, así como ZeroC Ice para Python y el IDE de Arduino. Si necesitas más información, consulta la documentación de sus respectivos proyectos.

Lo primero que necesitas es añadir nuestro repositorio de paquetes Debian: https://uclm-arco.github.io/debian/. Aquí encontrarás instrucciones para configurarlo y poder instalar nuestros paquetes.


A continuación, instala los paquetes de IceC y del Endpoint Serie para Ice e IceC, de la siguiente forma:

sudo apt-get install icec ice-serial icec-serial python-commodity

Dada la forma en que se distribuye el IDE de Arduino, será necesario crear un enlace en el directorio personal de librerías de Arduino a la librería de IceC, y otro al endpoint serie. Asumiendo que tu directorio de librerías es ~/Arduino/libraries, como suele ser el caso, puedes crear los enlaces con:

ln -s /usr/share/icec/arduino/ ~/Arduino/libraries/IceC
ln -s /usr/share/arduino/libraries/SerialEndpoint/ ~/Arduino/libraries/

A continuación, si arrancas el IDE, podrás ver las librerías de IceC y SerialEndpoint disponibles.

Arduino: cliente mínimo

Para entender de forma clara cómo funciona todo esto, escribirás un ejemplo muy sencillo. Básicamente es un Hello World! distribuido, usando Ice/IceC , en donde el cliente se ejecuta en el Arduino, y el servidor en tu PC. En esta sección abordaremos el cliente para Arduino.

Nota: puedes descargarte todo el código fuente y los archivos de configuración para este ejemplo (y los demás de esta serie de artículos) desde el repositorio de bitbucket:

hg clone https://bitbucket.org/arco_group/arduino-icec-idm

Directorio del Proyecto

Como en toda aplicación distribuida, se necesitará una especificación de la interfaz que vayamos a usar. En este caso, usaremos Slice como IDL. Así, lo primero que deberás hacer es crear un directorio donde almacenar el código de este ejemplo:

mkdir hello-world
cd hello-world

Interfaz IDL

Dentro de ese directorio, crea un fichero que contenga la interfaz del ejemplo. Su contenido será el siguiente (el fichero puedes llamarlo example.ice):

module Example {
  interface Hello {
    void sayHello();
  };
};

Como puedes observar, la interfaz solo tiene un método, sayHello, sin parámetros. A partir de esta interfaz (un fichero .ice), necesitas generar los stubs y skeletons que usarás dentro de tu sketch de Arduino. Para ello, usa la herramienta slice2c (que viene incluida en el paquete de IceC):

slice2c example.ice

Esto te generará un fichero llamado example.h, que podrás incluir directamente en el sketch.

Sketch de Arduino

Lo siguiente que necesitarás hacer será crear el fichero del proyecto que, de momento, estará vacío:

touch hello-world.ino

A continuación, arranca el IDE y abre el proyecto que acabas de crear. Incluye el siguiente boilerplate y compila, para comprobar que todo está en su sitio:

#include <IceC.h>
#include <SerialEndpoint.h>

#include "example.h"

void setup() {}
void loop() {}

Nota: recuerda que no es necesario que utilices el IDE como editor de texto; siempre puedes utilizar uno externo que se adapte más a tus necesidades y preferencias (como emacs, atom, gedit, etc.). Solo tienes que configurar el IDE para que sea consciente de ello.

Una vez comprobado que la interfaz es correcta y que el sketch compila, debemos añadir la parte de configuración de las dos librerías que vamos a usar, y el bucle de eventos.

Antes de nada, es necesario que crees las variables que vamos a emplear dentro del proyecto. Necesitarás una instancia del communicator de Ice (el broker del middleware), y un proxy al objeto remoto (que se ejecutará en el PC). Crea estas dos instancias globalmente, pues deberán ser accesibles desde el setup y el bucle de eventos:

Ice_Communicator ic;
Ice_ObjectPrx server;

A continuación, inicializa ambas variables en la función setup. Este proceso es análogo a la configuración de muchas otras librerías de Arduino, como el Serial.begin(…):

void setup() {
  Ice_initialize(&ic);
  SerialEndpoint_init(&ic);
[...]

También será necesario construir el proxy al objeto remoto. Este paso implica conocer la identidad (el nombre) del objeto remoto al que invocarás. Como todavía no lo has creado, podemos asumir que se llamará Server. El endpoint que se usará será, por supuesto, el serie. Un proxy de este tipo tiene la siguiente forma:

Identidad -d:serial

Donde lo único que puede variar es la identidad del objeto remoto. Por tanto, aún dentro de la función setup, construye el proxy:

[...]
  Ice_Communicator_stringToProxy(&ic, "Server -d:serial", &server);

Con esto, la parte de inicialización estaría lista. Solo queda hacer la invocación al objeto remoto. En este ejemplo, harás una invocación cada segundo, dentro del bucle de eventos; sin embargo se puede invocar en cualquier parte. Para ello, rellena la función loop con el siguiente contenido:

  Example_Hello_sayHello(&server);
  delay(1000);

Y listo. Puedes compilar y subir este código a tu Arduino. Si abres el monitor serie (115200 bauds, 8N1), verás que el sketch envía cada segundo un mensaje Ice (Ice usa IceP como protocolo, que es un protocolo binario, por lo que no verás apenas texto en claro).

En la siguiente entrada, implementarás el servidor en Python, y podrás ver las invocaciones realmente.