Category Archives: tinyOS

Indentación de código

0
Filed under Programación, tinyOS
Tagged as
He aquí un enlace que encontré acerca de estilos de indentación.

Cuando tenga algo de tiempo haré un “copy&paste” aquí (citando autor, claro :P ) por si acaso se perdiese, que me ha parecido muy curioso :-D

Indentación de código.

GLib IO Channels con C

0
Filed under GNU, Programación, tinyOS
Tagged as , ,

Introducción

Un patrón reactor (o también conocido como Distpatcher), es un patrón de programación utilizado cuando queremos atender varios eventos que pueden suceder de forma asíncrona y en ocasiones simultáneamente, pero queremos que ésos eventos sean atendidos sínconamente y, a ser posible, ordenadamente según el orden en que llegaron los eventos.
Posibles aplicaciones de éste patrón las podemos ver, por ejemplo, en cualquier servidor que queda a la escucha de un socket (de Internet o de cualquier otro tipo, evidentemente) y los va atendiendo según se vayan produciendo conexiones entrantes.
También podemos tener una aplicación en la que si no se produce ninguna entrada durante un tiempo determinado, la aplicación cierre, y en caso contrario, ejecute distintas operaciones según la fuente de entrada.
En éste último caso, podríamos hacer uso de la llamada al sistema select(), pero aquí explicaré como implementar dicho patrón utilizando GLib.

Patrón reactor en C.

El código de ejemplo para ésta receta es el que se puede ver a continuación:

  1. #include <stdio.h>
  2. #include <glib.h>
  3.  
  4. gboolean handler1(gpointer data) {
  5.   printf ("Manejador 1\n");
  6.   return TRUE;
  7. }
  8.  
  9. gboolean handler2(gpointer data) {
  10.   printf ("Manejador 2\n");
  11.   return TRUE;
  12. }
  13.  
  14. gboolean handler3(GIOChannel *source, GIOCondition condition, gpointer data) {
  15.   int fd;
  16.   char buff[100];
  17.  
  18.   for (fd = 0; fd < 100; fd++) {
  19.     buff[fd] = 0;
  20.   }
  21.  
  22.   printf ("Manejador 3\n");
  23.   fd = g_io_channel_unix_get_fd (source);
  24.   read(fd, buff, 100);
  25.   printf ("Buffer: %s", buff);
  26.   return TRUE;
  27. }
  28.  
  29. int main (void) {
  30.   gpointer data;
  31.  
  32.   g_timeout_add(1000, handler1, data);
  33.   g_timeout_add(500, handler2, data);
  34.  
  35.   g_io_add_watch(g_io_channel_unix_new(0), G_IO_IN, handler3, NULL);
  36.   g_main_loop_run(g_main_loop_new(NULL, FALSE));
  37.   return 0;
  38. }

El código que se muestra es bastante simple y fácil de entender. Al principio, después de las inclusiones de las cabeceras necesarias, se declaran tres funciones que consisten en los manejadores de los eventos capturados. Los dos primeros son exactamente iguales y lo único que hacen es escribir un mensaje por pantalla. El tercer manejador se llama cuando se introducen datos por teclado. Lo único que hace es inicializar un buffer en el que después copiaremos los datos introducidos y finalmente los imprimiremos. Podemos ver que éstas tres funciones devuelven TRUE, mas adelante se verá el porqué.

La “chicha” del asunto está en la función main, que es la que se encarga de asignar los manejadores anteriores a cada una de las señales y de inicializar el bucle de eventos.
Las llamadas a g_timeout_add asignan los dos primeros manejadores a un evento de tiempo, que se generará, respectivamente, cada segundo y medio segundo. Por su parte, la llamada a g_io_channel_unix_new asigna el tercer manejador a los eventos de escritura generados a través de la entrada estándar (el teclado).
Finalmente, la penúltima línea se encarga de poner en funcionamiento el bucle principal, creado por la función que se llama como primer parámetro. En ese momento, el programa se queda “congelado” ahí y lo único que hace es esperar a que se produzcan los eventos programados (los dos de temporización y la entrada por el teclado).
Antes comenté que los manejadores siempre devolvian TRUE. Ésto se debe a que si devolviesen FALSE, no volverían a ser llamados. Así conseguimos que los manejadores vuelvan a ser ejecutados cuando se producen de nuevo los eventos.

Referencias

NOTA: Entrada extraída de otra antigua entrada publicada por mi en CrySoL.

Creación de módulos e interfaces en nesC para TinyOS 2

0
Filed under Embedded, Programación, tinyOS
Tagged as , ,

Introducción

tinyOS se basa en el uso de módulos que se “cablean” (wiring) entre sí para dar lugar a la aplicación en sí. Este cableado se lleva a cabo en lo que se conoce como configuraciones y podemos tener montones de configuraciones distintas para una misma aplicación. De este modo, podemos conseguir que una misma aplicación pueda correr en distintas arquitecturas hardware sin apenas tocar código, solo modificando las configuraciones, lo que hará que se utilicen unos módulos u otros que se encargan del control a mas bajo nivel.

Básicamente, aquí hablaré de:

  • Módulos
  • Interfaces
  • Configuraciones

Todo el código de ejemplo que se usa en esta receta puede descargarse, vía subversión, del repositorio público de GoogleCode de la siguiente forma:

  1. yo@miMaquina:~$ svn checkout http://gn-cillos.googlecode.com/svn/trunk/tinyOS

y con ésto ya podremos seguir todos los ejemplos dados a continuación.

Interfaces

Las interfaces se pueden definir como la parte “visible” de los módulos. No se puede acceder a ninguna función de un módulo si antes no ha sido definida en una interfaz.
Una interfaz puede ser provista por varios módulos distintos. Por ejemplo, podemos tener una interfaz de comunicaciones que abstraiga los procedimientos de envío y recepción de mensajes y varios módulos que hagan uso de esa interfaz, uno para cada tipo de hardware subyacente (por ejemplo no se maneja igual el hardware de comunicaciones de una mica2 que el de una telos). Los métodos serán los mismos y se llamarán igual (la interfaz es la misma), pero la implementación interna puede ser totalmente distinta. Nosotros lo veremos como una caja negra.

En nuestro ejemplo se utilizan dos interfaces: interfaz2, en el archivo interfaz2.nc e interfazPRB en el archivo interfazPRB.nc. Las implementaciones son las que siguen:

  1. interface interfaz2 {
  2.   command void i2c1();
  3.   command uint16_t i2c2(int c);
  4.   command uint16_t i2c3(char d);
  5. }

para interfaz2.nc y

  1. interface interfazPRB {
  2.   command void comando1();
  3.   command int comando2(int c);
  4. }

para interfazPRB.nc

Bueno. Se puedes ver, escribir una un interfaz es bastante simple. Lo único que hay que hacer es especificar el nombre de la y los comandos de que consta. Estos comandos son los prototipos (como se llama en ANSI C) de las funciones precedidos de la palabra reservada command. Si quisiesesmos declarar señales, utilizariamos el modificador signal. El nombre del archivo debe de ser el mismo que el del interfaz, respetando las mayúsculas y con extensión .nc.

Módulos

Los módulos implementan bloques funcionales generalmente de un nivel de abstracción más bajo que el bloque que los referencia. Un mismo módulo puede proveer varias interfaces y por lo tanto puede implementar distintas funcionalidades (Por ejemplo el módulo AMSender provee las interfaces Packet, AMPacket, AMSend, etc…). Lo lógico es que todos los conjuntos de funciones (interfaces) que implementa un módulo guarden algún tipo de relación.

En nuestro caso, utilizamos dos módulos: modulo2C, implementado en modulo2C.nc y moduloC implementado en moduloC.nc.
Éste es el contenido de los archivos:

  1. module modulo2C {
  2.   provides {
  3.     interface interfaz2;
  4.     /*
  5.       Única interfaz proporcionada por el módulo
  6.     */
  7.   }
  8. }
  9.  
  10. implementation
  11. {
  12.   command void interfaz2.i2c1() {
  13.     int v1 = 5;
  14.     int v2;
  15.     v2 = v1;
  16.   }
  17.  
  18.   command uint16_t interfaz2.i2c2(int c) {
  19.     return (uint16_t)2*c;
  20.   }
  21.  
  22.   command uint16_t interfaz2.i2c3(char d) {
  23.     return (uint16_t)d*d*d;
  24.   }
  25. }

para modulo2C y

  1.  
  2. module moduloC {
  3.   provides interface interfazPRB;
  4.   uses {
  5.     interface interfaz2;
  6.     interface Leds;
  7.   }
  8. }
  9.  
  10. implementation {
  11.   command void interfazPRB.comando1() {
  12.     call Leds.led0Toggle();
  13.     call Leds.led1Toggle();
  14.     call Leds.led2Toggle();
  15.   }
  16.  
  17.   command int interfazPRB.comando2(int c) {
  18.     call Leds.set((uint8_t)c);
  19.     return 0;
  20.   }
  21. }

Cada uno de los módulos se compone de dos partes. La primera es la declaración. En ella se especifica el nombre del módulo, así como las interfaces que provee y las interfaces que usa. En el caso de moduloC, por ejemplo, podemos ver que la interfaz que proporciona es la interfaz interfacePRB y utiliza las interfaces interfaz2 y Leds.
A continuación sigue la implementación. Aquí debe hacerse la implementación de cada uno de los comandos especificados en la interfaz. En éste caso no hay señales. Si las hubiese, la forma de lanzarlas es llamarlas como funciones normales (precedidas de la palabra reservada signal) cuando sea necesario. En éste caso, como se trata solo de un ejemplo, las funciones son totalmente triviales.

No queda mucho mas que comentar acerca de los módulos. Básicamente, después de ésto solo faltaría escribir la configuración con el wiring entre módulos e interfaces. ¿Y qué es eso?, pues ahora mismo lo explico.

Configuraciones

Como se ha dicho anteriormente, una aplicación escrita para tinyOS se compone de implementación de módulos y de configuraciones. En algunos casos, pueden hacerse aplicaciones solo con archivos de configuraciones. Un archivo de configuración consiste básicamente en un archivo formado por los módulos (o componentes) a utilizar en la aplicación y la relación que hay entre dichos módulos y las interfaces utilizadas por otros módulos.

En un principio esto puede parecer bastante lioso, así que nada mejor que verlo con algún ejemplo. Veamos primero el archivo de configuración moduloEjm.nc:

  1. configuration moduloEjmC {
  2.   provides interface interfazPRB;
  3. }
  4.  
  5. implementation  {
  6.  
  7.   components LedsC;
  8.   components modulo2C;
  9.   components moduloC;
  10.  
  11.   moduloC.Leds -> LedsC;
  12.   moduloC.interfaz2 -> modulo2C;
  13.  
  14.   interfazPRB = moduloC;
  15. }

Paso a paso para no liarnos:

Lo primero que tenemos que hacer es indicar que estamos hablando de una configuración, en éste caso moduloEjmC. Como siempre, dicho nombre coincide con el del archivo en el que se aloja. Ésta configuración, provee una interfaz (aunque podrian ser mas), la interfaz interfazPRB.
En la parte de implementación vemos que utilizamos tres módulos o componentes: LedsC, modulo2C y moduloC. Éstos son los módulos cuya funcionalidad necesitaremos para llevar a cabo las tareas de la configuración en la que estamos trabajando.

Después nos encontramos con la parte de wiring. Aquí tenemos dos conexiones:

  • moduloC.Leds -> LedsC;
  • moduloC.interfaz2 -> modulo2C;

¿Qué hace ésto? fácil, hemos conectado la interfaz Leds utilizada en la implementación del módulo moduloC con el componente (o módulo) LedsC que provee dicha interfaz y que hemos declarado anteriormente en la sección components. Con ésto, cada vez que invoquemos a alguno de los comando de ésta interfaz, lo que en realidad estaremos haciendo será llamar a los métodos con dicho nombre que haya implementados en el módulo LedsC. Si tuviésemos otro módulo con la misma interfaz, podriamos haberlo utilizado indistintamente, con la única diferencia de la implementación que esconda detrás, claro.

La segunda conexión ya queda algo mas clara, conecta el interfaz interfaz2 de moduloC con el módulo modulo2C que provee dicha interfaz y que nosotros mismos escribimos.

Finalmente, en la última línea, podemos ver una asignación distinta a todas las vistas anteriormente. Ésta asignación lo único que hace es conectar la interfaz que se provee (interfazPRB) con el módulo que proveerá la funcionalidad a dicha interfaz, que en éste caso es moduloC.

Ahora, lo único que falta es utilizar todo ésto:

  1. configuration ejmAppC{}
  2.  
  3. implementation
  4. {
  5.   components MainC, ejmC;
  6.   components moduloEjmC;
  7.  
  8.   ejmC -> MainC.Boot;
  9.   ejmC.interfazPRB -> moduloEjmC;
  10. }

Aquí tenemos el contenido de ejmAppC.nc, que tiene la configuración ejmAppC.

Esta configuración podemos ver que no provee interfaz alguna, pero que sí usa componentes, en concreto MainC, ejmC y moduloEjmC. Los dos últimos son familiares ;-)

Después tenemos los wirings: uno que conecta la interfaz Boot del módulo MainC (utilizado para informar de que tinyOS ha arrancado y demás) con el módulo de mayor nivel de nuestra aplicación (la aplicación en si), ejmC y otro que conecta la interfaz interfazPRB que utiliza la aplicación, con el módulo (aunque lo hayamos “implementado” en un archivo de configuración, lo estamos tratando como un módulo, ya que esa configuración corresponde a la de un módulo) moduloEjmC.

Con ésto, ya solo nos falta implementar las funciones de nuestro programita propiamente dicho:

  1. module ejmC {
  2.   uses interface Boot;
  3.   uses interface interfazPRB;
  4. }
  5.  
  6. implementation {
  7.   event void Boot.booted() {
  8.     call interfazPrb.comando1();
  9.   }
  10. }

Aquí puedes ver cómo se define un módulo llamado ejmC y que utiliza las interfaces Boot e interfazPRB que ya conectamos anteriormente a cada uno de los módulos que queríamos que nos diesen la funcionalidad.

A continuación, en la parte de la implementación tan solo tenemos que implementar las funciones y comandos que necesitemos para dar la funcionalidad que queramos a nuestro programita.

¿Qué por qué este es el módulo de mayor nivel? Bueno, por nada en especial. Simplemente que implementa la interfaz Boot del módulo MainC, quien lanzará el evento Boot.booted cuando tinyOS termine de arrancar y por tanto entregará el control del hilo principal a dicho evento, comenzando la ejecución del programa por ahí.

Referencias

NOTA: Entrada extraída de otra antigua entrada publicada por mi en CrySoL.