Anda di halaman 1dari 33

ANEXO C

INTRODUCCIN A LA PROGRAMACIN
EN TINYOS: NESC.
La gran mayora de redes de sensores utilizan para su desarrollo el sistema
operativo TinyOS. Tanto el sistema operativo como las distintas aplicaciones que se
puedan implementar con la red de sensores, se encuentran escritos en el lenguaje
de programacin nesC, un lenguaje sintcticamente similar a C y diseado
especficamente para este tipo de redes.

A lo largo de este anexo, se van a estudiar los pilares fundamentales de la


programacin en el lenguaje nesC. Para ello, se irn explicando los distintos
conceptos bsicos de este lenguaje, ilustrndolos con diversos ejemplos prcticos
que ayuden a su comprensin. Se ha de tener en cuenta que al igual que en el
resto del proyecto, se ha utilizado el sistema operativo Ubuntu 8.04, por lo que
cualquiera de los comandos aqu expuestos es posible que vare su sintaxis en otros
sistemas. La informacin que aqu se expone puede consultarse y ampliarse en
[44].

1 Descripcin del lenguaje.


Una aplicacin en nesC consiste en uno o ms componentes que se
ensamblan (wired) para generar un archivo ejecutable. El compilador de nesC crea

58
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

este archivo ejecutable, ensamblando los componentes durante el proceso de


compilacin.

Un componente provee y/o usa interfaces. Las interfaces son archivos


identificados por la palabra clave interface al comienzo del cdigo. Contienen
funciones de dos clases: comandos y eventos, lo que les otorga un carcter
bidireccional. Por un lado, especifican una serie de comandos que han de ser
implementadas por el componente proveedor de la interfaz y, por otro, especifican
una serie de eventos que han de ser implementadas por el componente que usa
dicha interfaz. Los comandos y los eventos son conceptos similares al concepto de
funcin en el lenguaje C.

Los comandos contenidos en una interfaz usada por un componente, son


las funciones o mtodos que este componente necesita invocar para poder
desempear su funcin. Asimismo, el componente deber implementar los
eventos contenidos en dicha interfaz.

El ensamblado de componentes se realiza conectando las interfaces usadas


por unos componentes con las interfaces provistas por otros. Un componente
puede usar y proveer tantas interfaces como desee, ya sean interfaces distintas o
instancias de la misma interfaz.

Existen dos clases de componentes: los mdulos y las configuraciones. El


componente de tipo mdulo es del que hemos estado hablando hasta ahora y
consta de dos partes: una primera parte en la que especifica las interfaces que usa
o provee y, una segunda parte, en la que se implementan dichas interfaces. Por
otro lado, el componente de tipo configuracin es el que se utiliza para representar
el ensamble entre las interfaces provistas y usadas por los componentes de tipo
mdulo de una aplicacin.

El motivo de distinguir entre mdulos y configuraciones es permitir


programar aplicaciones a partir de implementaciones ya existentes. Por ejemplo,
un diseador podra proveer configuraciones que simplemente ensamblasen
mdulos, sin que ninguno de los cuales hubiese sido implementado. Del mismo
modo, otro diseador podra simplemente proveer un conjunto de mdulos
dispuestos para poder ser usados en diversas aplicaciones.

Toda aplicacin en TinyOS est compuesta de, al menos, un componente de


tipo mdulo, un Makefile y un componente de tipo configuracin. Este
componente de configuracin, conocido como configuracin de alto nivel, es el
encargado de ensamblar todos los componentes de la aplicacin. El Makefile, al
igual que en el lenguaje C, es un archivo utilizado para compilar.

59
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

2 Comandos bsicos.
Antes de introducir un ejemplo prctico que aclare todas las ideas
expuestas, vamos a describir los principales comandos para compilar e instalar
aplicaciones en TinyOS:

Compilar el cdigo:

$ make micaz

Abrimos un terminal en la carpeta donde se encuentre la aplicacin y, a


continuacin, ordenamos al sistema que compile dicha aplicacin con el comando
make. Adems, hemos de introducir junto al comando la plataforma con la que se
est trabajando.

Instalacin en el mote:

$ make micaz reinstall mib520,<PORT>

Una vez conectada la placa principal a la placa programadora, introducimos


el comando anterior para ordenar al sistema que programe el mote con el archivo
binario que se gener durante la compilacin.

En la lnea anterior, <PORT> representa el puerto serie virtual que el


sistema cre al conectar la placa programadora a un puerto USB del PC. Siempre
que conectemos una placa programadora o una estacin base al PC por un puerto
USB, se crearn dos puertos serie virtuales numerados como X y X+1, donde X
representa el puerto para la programacin de los motes y X+1 el puerto destinado
a la comunicacin con el PC. Para consultar el nmero de puerto, hemos de
dirigirnos a la carpeta /dev. Una vez en el directorio, buscamos un archivo llamado
ttyUSBX, donde X representa el puerto de programacin. De esta manera,
suponiendo que al conectar la placa programadora se hubiesen creado los puertos
virtuales 0 y 1, el comando quedara as:

$ make micaz reinstall mib520,/dev/ttyUSB0

Existe tambin la posibilidad de realizar el proceso de compilacin y la


instalacin en el mote en un slo paso. Para ello, usaramos el siguiente comando:

$ make micaz install mib520,/dev/ttyUSB0

60
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

Por otra parte, si deseamos asignar un nmero que identifique al nodo que
estamos programando, podemos hacerlo de la siguiente manera:

$ make micaz install,N mib520,/dev/ttyUSB0

donde N representa el identificador que queremos asignar. Este identificador es lo


que se conoce como direccin AM. Se debe evitar en la manera de lo posible
asignar el identificador 0 a un mote, ya que ste suele estar reservado para la
estacin base y podra dar problemas durante la ejecucin de la aplicacin.
Asimismo, se ha de evitar tambin conectar la placa principal a la placa
programadora en la posicin ON.

Por ltimo, se ha de tener en cuenta que existe la posibilidad de que los


puertos serie virtuales creados por el sistema no estn disponibles para escritura,
por lo que no podremos programar los nodos hasta que los habilitemos. El permiso
de escritura se activa con el siguiente comando:

$ sudo chmod 666 <PUERTO>

que en el ejemplo que estamos tratando sera:

$ sudo chmod 666 /dev/ttyUSB0

Documentacin de la aplicacin:

$ make micaz docs

Al introducir este comando se crea automticamente la documentacin


relativa al cdigo fuente de la aplicacin. La herramienta nesdoc, encargada de
este proceso, muestra grficamente la estructura y composicin de los
componentes de tipo configuracin. La documentacin generada se almacena en el
directorio /opt/tinyos-2.1.0/doc/nesdoc/micaz.

Eliminar archivos binarios:

$ make clean

Usando este comando se eliminan todos los archivos binarios que se han
creado durante el proceso de compilacin.

61
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

3 Un primer ejemplo: Blink.


Llegados a este punto, vamos a analizar un ejemplo que permita ver de
forma prctica lo anteriormente descrito. El ejemplo que vamos a estudiar se
encuentra en la carpeta /opt/tinyos-2.1.0/apps/Blink. En lo sucesivo, salvo que se
indique lo contrario, los directorios a los que se haga referencia supondremos que
se encuentran dentro de la carpeta /opt/tinyos-2.1.0.

El cdigo situado en la carpeta Blink, implementa una aplicacin consistente


en conmutar los tres leds del mote programado, de modo que simule un contador
binario que se incremente con una frecuencia de 0,25Hz, es decir, cuenta de 0 a 7
en dos segundos.

La aplicacin Blink est constituida por dos componentes: un componente


de tipo mdulo llamado BlinkC.nc y otro llamado BlinkAppC.nc de tipo
configuracin.

Hemos de recordar que toda aplicacin escrita en nesC requiere de un


componente de configuracin de alto nivel. En este caso, BlinkAppC.nc es el
componente de configuracin, mientras que BlinkC.nc es el archivo que contiene la
implementacin de la aplicacin. Por tanto, la funcin de BlinkAppC.nc es
ensamblar el componente BlinkC.nc con el resto de componentes que requiere la
aplicacin.

3.1 La Configuracin de alto nivel.


A continuacin, vamos a mostrar el cdigo de Blink y comentaremos las
distintas caractersticas del mismo. El primer archivo que vamos a examinar es el
componente de tipo configuracin, el archivo BlinkAppC.nc:

configuration BlinkAppC {
}
implementation
{
components MainC, BlinkC, LedsC;
components new TimerMilliC() as Timer0;
components new TimerMilliC() as Timer1;
components new TimerMilliC() as Timer2;

BlinkC -> MainC.Boot;

BlinkC.Timer0 -> Timer0;


BlinkC.Timer1 -> Timer1;
BlinkC.Timer2 -> Timer2;
BlinkC.Leds -> LedsC;
}
Lo primero que se observa es la palabra clave configuration, que indica que
el archivo es un componente de configuracin. Junto a ella se encuentra el nombre
62
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

del archivo. Este bloque se utiliza para declarar interfaces que se van a usar y/o
proveer, al igual que se hace en los componentes de tipo mdulo. En este caso, no
es necesario hacer nada de esto, por lo que se deja vaco.

El bloque implementation contiene la implementacin del componente de


configuracin. Las lneas iniciadas con la palabra clave components, indican los
componentes necesarios en la aplicacin para proveer y usar las interfaces. En este
caso son MainC, BlinkC, LedsC y tres instancias del componente TimerMilliC
identificadas como Timer0, Timer1 y Timer2. TimerMilliC es un componente
genrico y, a diferencia de los no genricos, puede ser instanciado ms de una vez.
Los componentes genricos pueden recibir como argumentos variables y
constantes, aunque en este caso no es necesario. A cada una de las instancias se le
asigna un identificador con la palabra clave as.

El resto de BlinkAppC consiste en ensamblar las interfaces usadas por el


componente BlinkC a las interfaces provistas por los componentes MainC,
TimerMilliC y LedsC.

La lnea del cdigo:

BlinkC.Leds -> LedsC;


ensambla la interfaz Leds usada por el componente BlinkC con la interfaz Leds
provista por el componente LedsC.

De manera equivalente, la lnea:

BlinkC -> MainC.Boot;


ensambla la interfaz Boot usada por el componente BlinkC con la interfaz Boot
provista por el componente MainC.

Por ltimo, cada una de las tres lneas del tipo:

BlinkC.TimerX -> TimerX;


ensambla la interfaz Timer provista por el componente TimerMilliC con la interfaz
Timer usada por el componente BlinkC. Estas ltimas sentencias pueden inducir a
error, porque mientras que BlinkC.TimerX identifica la interfaz del componente
BlinkC que se ensambla, la otra parte de la sentencia no identifica a la interfaz de
TimerMilliC que se ensambla, sino que representa la instancia del componente
TimerMilliC que provee la interfaz. Para aclarar esta idea, vemos cmo es la forma
general de la expresin para ensamblar interfaces:

A.a -> B.b

63
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

A representa el componente de la interfaz a y B el componente de la


interfaz b, ambas ensambladas mediante la operacin ->. Se deben identificar las
distintas instancias de una interfaz cuando un componente usa o provee mltiples
interfaces. Sin embargo, en caso de que se ensamble una nica instancia, el
nombre es opcional. Por ejemplo, en la aplicacin que estamos analizando, al usar
BlinkC una nica instancia de Leds, podramos haber puesto en lugar de:

BlinkC.Leds -> LedsC;

la siguiente sentencia:
Blink -> LedsC.Leds;

o en su forma ms completa:
BlinkC.Leds -> LedsC.Leds;
No ocurre lo mismo con la interfaz Timer, ya que en el lado correspondiente
al componente BlinkC, al existir mltiples instancias de la interfaz, se necesita
nombrarlas para poder distinguirlas. En el lado correspondiente al componente
TimerMilliC no es necesario nombrarlas, ya que cada una de las instancias de este
componente provee una nica interfaz Timer. Por lo tanto, la siguiente sentencia
generara un error:

BlinkC -> Timer0.Timer;

puesto que BlinkC no identifica cul de las interfaces est ensamblando.


Por otra parte, el sentido de la flecha es siempre de usuario a proveedor de
la interfaz, es decir, las siguientes lneas de cdigo son equivalentes:

Timer0 <- BlinkC.Timer0;

y
BlinkC.Timer0 -> Timer0;

64
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

3.2 El Mdulo de Blink.


Veamos ahora el cdigo del componente de tipo mdulo BlinkC.nc:

module BlinkC {
uses interface Timer<TMilli> as Timer0;
uses interface Timer<TMilli> as Timer1;
uses interface Timer<TMilli> as Timer2;
uses interface Leds;
uses interface Boot;
}
implementation
{
event void Boot.booted()
{
call Timer0.startPeriodic( 250 );
call Timer1.startPeriodic( 500 );
call Timer2.startPeriodic( 1000 );
}

event void Timer0.fired()


{
call Leds.led0Toggle();
}

event void Timer1.fired()


{
call Leds.led1Toggle();
}

event void Timer2.fired()


{
call Leds.led2Toggle();
}
}
La primera parte del cdigo establece, con la palabra clave module, que el
archivo es un componente de tipo mdulo llamado BlinkC y declara las interfaces
que provee y las que usa, la cuales fueron previamente ensambladas en el archivo
de configuracin. El componente BlinkC usa tres instancias de la interfaz
Timer<TMilli>1 a las que se han asignado los nombres Timer0, Timer1 y Timer2, as
como la interfaz Leds y la interfaz Boot.

En la segunda parte del cdigo, se implementan los eventos de la aplicacin


y se realizan las llamadas a los comandos. BlinkC puede llamar a cualquier
comando declarado en las interfaces que usa y, adems, debe implementar
cualquier evento declarado en stas. Es por ello por lo que implementa el evento
Boot.booted(), incluido en la interfaz Boot, y el evento TimerX.fired(), contenido en
cada una de las instancias de la interfaz Timer. La interfaz Leds, sin embargo, no

1
<TMilli> especifica que la unidad de tiempo de la interfaz Timer ser el milisegundo.

65
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

declara ningn evento por lo que directamente se pueden invocar sus comandos
con la palabra reservada call.

Si compilamos la aplicacin, la salida por pantalla debera ser la siguiente:

mkdir -p build/micaz
compiling BlinkAppC to a micaz binary
ncc -o build/micaz/main.exe -Os -Wall -Wshadow -Wnesc-all -
target=micaz -fnesc-cfile=build/micaz/app.c -board=micasb -
DDEFINED_TOS_AM_GROUP=0x22 --param max-inline-insns-
single=100000 -DIDENT_APPNAME=\"BlinkAppC\" -
DIDENT_USERNAME=\"invitado\" -DIDENT_HOSTNAME=\"mvillagranm\"
-DIDENT_USERHASH=0xa308053eL -DIDENT_TIMESTAMP=0x4985963fL -
DIDENT_UIDHASH=0x71a2a6deL -fnesc-dump=wiring -fnesc-
dump='interfaces(!abstract())' -fnesc-
dump='referenced(interfacedefs, components)' -fnesc-
dumpfile=build/micaz/wiring-check.xml BlinkAppC.nc -lm
compiled BlinkAppC to build/micaz/main.exe
2052 bytes in ROM
51 bytes in RAM
avr-objcopy --output-target=srec build/micaz/main.exe
build/micaz/main.srec
avr-objcopy --output-target=ihex build/micaz/main.exe
build/micaz/main.ihex
writing TOS image

4 Tipos de variables.
Al compilar una aplicacin se genera una imagen binaria, que gestionar
todo el hardware durante la ejecucin de la misma. Por tanto, un mote no puede
ejecutar varias aplicaciones simultneamente.

Otra caracterstica a destacar es que en un mote no existe separacin


hardware de la memoria, es decir, no hay un espacio de direccionamiento asignado
al usuario y otro distinto asignado al sistema, sino que ambos comparten un
espacio global de direccionamiento. Es por ello, por lo que se trata de evitar el paso
de punteros entre componentes: la mejor forma de proteger la memoria es
compartirla lo menos posible.

Un componente puede declarar y modificar variables. Cualquier variable


que declare un componente es privada, nadie puede acceder a ella directamente.
La nica forma que tienen dos componentes de interactuar es mediante las
interfaces.

BlinkC no declara ninguna variable, por lo que vamos a realizar una pequea
modificacin en el cdigo para ilustrar el uso de variables. En lugar de usar tres
timers, vamos a usar slo uno y declararemos una variable que indique qu led
debe conmutar. Al igual que en C, las variables deben declararse antes de ser

66
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

usadas, por lo que las colocamos al comienzo del cdigo. El cdigo de la parte de
implementacin del componente tipo mdulo quedara as (el resto no cambia):

implementation
{
uint8_t counter = 0;

event void Boot.booted()


{
call Timer0.startPeriodic( 250 );
call Timer1.startPeriodic( 500 );
call Timer2.startPeriodic( 1000 );
}

event void Timer0.fired()


{
counter++;
if (counter & 0x1) {
call Leds.led0On();
}
else {
call Leds.led0Off();
}
if (counter & 0x2) {
call Leds.led1On();
}
else {
call Leds.led1Off();
}
if (counter & 0x4) {
call Leds.led2On();
}
else {
call Leds.led2Off();
}
}

event void Timer1.fired(){


}

event void Timer2.fired(){


}
}

Las variables en nesC, a diferencia de C, especifican el tamao en su


declaracin. TinyOS evita declarar variables tipo int, por ejemplo, ya que esto
puede producir errores si el tamao de la variable es distinto en funcin de la
plataforma en la que se est trabajando. Un ejemplo de esto puede verse en los
motes Mica o Telos, cuyo tipo int es de 16 bits, mientras que los IntelMote2
asignan un tamao de 32 bits. De este modo, TinyOS se convierte en un sistema
real multiplataforma. La Tabla 1 muestra distintos tipos de variables de TinyOS en
funcin de su tamao y de su signo.

67
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

8 bits 16 bits 32 bits 64 bits


Con signo int8_t int16_t int32_t int64_t
Sin signo uint8_t uint16_t uint32_t uint64_t
Tabla 1. Tamao de las variables.

Adems del tipo int, existen los tipos float y double (slo en algunas
plataformas) y un tipo booleano que se declara usando la palabra reservada bool.

4.1 Optimizar el cdigo.


En el cdigo modificado podemos observar que existe una variable sin signo
de 8 bits que almacena el valor del contador. Cuando el timer completa el periodo
de disparo, esta variable se incrementa y conmuta los leds necesarios para mostrar
su valor.

Si compilamos el cdigo y lo programamos en un mote, veremos que se


comporta exactamente igual que antes, con la diferencia de que estamos usando
un nico timer.

Otro modo ms sutil de hacer esto, hubiese sido sustituir el evento


Timer0.fired()por este otro:

event void Timer0.fired()


{
counter++;
call Leds.set(counter);
}
Con el comando set() de la interfaz Leds se encienden o apagan
directamente los leds necesarios para mostrar en binario el valor de la variable
contador.

Como ahora no son necesarios los otros dos timers, los eliminamos del
cdigo para que no consuman ni recursos de la CPU ni memoria. El cdigo de BinkC
sera, por tanto:

module BlinkC {
uses interface Timer<TMilli> as Timer0;
uses interface Leds;
users interface Boot;
}
implementation
{
uint8_t counter = 0;

event void Boot.booted()


{
call Timer0.startPeriodic( 250 );
}

68
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

event void Timer0.fired()


{
counter++;
call Leds.set(counter);
}
}
Si tratamos de compilar este cdigo, el compilador lanzar un error ya que
BlinkAppC.nc ensambla componentes de BlinkC.nc que ya no existen:

mkdir -p build/micaz
compiling BlinkAppC to a micaz binary
ncc -o build/micaz/main.exe -Os -Wall -Wshadow -Wnesc-all -
target=micaz -fnesc-cfile=build/micaz/app.c -board=micasb -
DDEFINED_TOS_AM_GROUP=0x22 --param max-inline-insns-
single=100000 -DIDENT_APPNAME=\"BlinkAppC\" -
DIDENT_USERNAME=\"invitado\" -DIDENT_HOSTNAME=\"mvillagranm\"
-DIDENT_USERHASH=0xa308053eL -DIDENT_TIMESTAMP=0x4985bf93L -
DIDENT_UIDHASH=0x09d5a094L -fnesc-dump=wiring -fnesc-
dump='interfaces(!abstract())' -fnesc-
dump='referenced(interfacedefs, components)' -fnesc-
dumpfile=build/micaz/wiring-check.xml BlinkAppC.nc -lm
In component `BlinkAppC':
BlinkAppC.nc:54: cannot find `Timer1'
BlinkAppC.nc:55: cannot find `Timer2'
make: *** [exe0] Error 1
Por lo tanto, hemos de eliminar las lneas del componente de configuracin
donde se declaran estos timers y tambin aquellas en las que se ensamblan. Ahora
al compilar tendremos:

mkdir -p build/micaz
compiling BlinkAppC to a micaz binary
ncc -o build/micaz/main.exe -Os -Wall -Wshadow -Wnesc-all -
target=micaz -fnesc-cfile=build/micaz/app.c -board=micasb -
DDEFINED_TOS_AM_GROUP=0x22 --param max-inline-insns-
single=100000 -DIDENT_APPNAME=\"BlinkAppC\" -
DIDENT_USERNAME=\"invitado\" -DIDENT_HOSTNAME=\"mvillagranm\"
-DIDENT_USERHASH=0xa308053eL -DIDENT_TIMESTAMP=0x4985c05bL -
DIDENT_UIDHASH=0x265c5d70L -fnesc-dump=wiring -fnesc-
dump='interfaces(!abstract())' -fnesc-
dump='referenced(interfacedefs, components)' -fnesc-
dumpfile=build/micaz/wiring-check.xml BlinkAppC.nc -lm
compiled BlinkAppC to build/micaz/main.exe
1936 bytes in ROM
34 bytes in RAM
avr-objcopy --output-target=srec build/micaz/main.exe
build/micaz/main.srec
avr-objcopy --output-target=ihex build/micaz/main.exe
build/micaz/main.ihex
writing TOS image
Comparando el consumo de memoria de los dos programas, se aprecia
como ste ltimo consume algo menos de memoria al necesitar slo un timer.

69
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

5 Interfaces.
En la aplicacin Blink se usan las interfaces Boot, Timer y Leds. Estas
interfaces se encuentran en la carpeta /tos/interfaces. Vemos como son:

Boot

interface Boot {
event void booted();
}
Timer

interface Timer
{
// basic interface
command void startPeriodic( uint32_t dt );
command void startOneShot( uint32_t dt );
command void stop();
event void fired();

// extended interface omitted


}
Leds

interface Leds {

/**
* Turn LED X on, off, or toggle its present state.
*/
async command void led0On();
async command void led0Off();
async command void led0Toggle();

async command void led1On();


async command void led1Off();
async command void led1Toggle();

async command void led2On();


async command void led2Off();
async command void led2Toggle();

/**
* Get/Set the current LED settings as a bitmask.
* Each bit corresponds to whether an LED is on; bit 0
* is LED 0, bit 1 is LED 1, etc.
*/
async command uint8_t get();
async command void set(uint8_t val);

}
El evento booted() se produce una vez que el sistema se ha inicializado y
en Blink es el encargado de iniciar el timer de cada una de las instancias de la

70
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

interfaz Timer. El comando startPeriodic() especifica el periodo de disparo del


timer en milisegundos (es en milisegundos por haber usado el parmetro <TMilli>
de la interfaz).

Para llamar a un comando de una interfaz se usa la palabra clave call y para
llamar a un evento se utiliza la palabra clave signal. BlinkC no provee ninguna
interfaz, luego en el cdigo no aparecer ninguna llamada con signal. Todas las
llamadas que aparezcan en el cdigo se realizarn con call, puesto que son
llamadas a los comandos de las interfaces provistas por otros componentes.

En la aplicacin Blink se emplean tres instancias de la interfaz Timer, por lo


que se deben implementar tres instancias del evento Timer.fired(). En nesC,
siempre que se llame a una funcin de una interfaz se har como interfaz.funcion().

6 Tareas.
Hasta el momento, todos los ejemplos que hemos analizado contenan
cdigo sncrono (sync). Componentes como los timers o la secuencia de arranque
(Boot), envan seales que generan eventos, realizan alguna accin y retornan. Un
cdigo sncrono, cuando comienza su ejecucin, no cede el control de la CPU a
ninguna otra parte de cdigo hasta que no se haya ejecutado al completo. Este
mecanismo permite a TinyOS minimizar el consumo de memoria RAM y simplificar
el cdigo de las aplicaciones. No obstante, tiene la desventaja de que cuando el
cdigo es muy extenso, mantiene bloqueada la CPU durante un largo periodo de
tiempo sin permitir que otro cdigo se ejecute, lo que puede afectar
negativamente a la capacidad de respuesta del sistema.

El uso de cdigos sncronos no es til en aplicaciones extensas. Un


componente debe ser capaz de dividir su ejecucin en distintas fases, de modo que
no mantenga bloqueada la CPU durante un largo periodo de tiempo. En ocasiones,
un componente necesita realizar alguna accin pero puede aplazarla para ms
tarde, permitiendo as a la CPU llevar a cabo otras actividades. La operacin se
pone en cola y se ejecutar cuando se hayan completado las que estn en la cola
antes que ella. Este tipo de operaciones es lo que se denominan tareas. Una tarea
es, por tanto, una porcin de cdigo que un componente puede ejecutar ms
tarde.

Recordemos el evento de finalizacin del periodo del timer en BlinkC:

event void Timer0.fired()


{
call Leds.led0Toggle();
}

71
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

y modifiqumoslo por este otro:


event void Timer0.fired() {
uint32_t i;
for (i = 0; i < 400001; i++) {
call Leds.led0Toggle();
}
}
Ahora cada vez que el timer completa un periodo, el led0 conmuta 400001
veces en lugar de una sla. Esto provoca que haya mucha latencia en la
conmutacin de los leds 1 y 2. El problema se debe a que la ejecucin del programa
est interfiriendo en el funcionamiento del timer. La manera de resolver esto es
usando una tarea.

Una tarea tiene la sintaxis siguiente:

task void nombreTarea(){



}
Las tareas no deben devolver nada (void) y no pueden recibir argumentos.
Para aplazar la ejecucin de una tarea se usa el siguiente comando:

post nombreTarea();
El comando post devuelve un valor de tipo error_t, cuyo valor puede ser
SUCCESS o FAIL. Se devuelve FAIL si y slo si la tarea ha sido puesta en cola pero no
se ha ejecutado an.

Un componente puede incluir una tarea en un comando, un evento e


incluso otra tarea. Si reescribimos el cdigo anterior de Timer0.fired() en trminos
de tareas quedara como:

task void computeTask() {


uint32_t i;
for (i = 0; i < 400001; i++) {}
}

event void Timer0.fired() {


call Leds.led0Toggle();
post computeTask();
}
El comando post coloca la tarea en una cola interna que se rige segn la
estructura FIFO. Cuando una tarea es ejecutada, debe completarse antes de
comenzar con una nueva tarea. Una tarea no debe estar ejecutndose durante
largos periodos de tiempo ya que aunque una tarea no interrumpe a otra, s que
puede ser detenida por una interrupcin hardware.

Otro ejemplo de cdigo que usa tareas es el siguiente:

72
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

uint32_t i;

task void computeTask() {


uint32_t start = i;
for (;i < start + 10000 && i < 400001; i++) {}
if (i >= 400000) {
i = 0;
}
else {
post computeTask();
}
}
Este programa divide una tarea en tareas ms pequeas, de modo que en
cada llamada a la tarea computeTask() se lleven a cabo 10000 iteraciones del bucle.
Destacar que la variable entera i se comporta como si fuera static.

7 Funciones internas.
Los comandos y los eventos son la nica forma que tiene un componente de
llamar a una funcin en una interfaz provista por otro componente. En ocasiones,
hemos de definir funciones internas para uso propio. Estas funciones podemos
llamarlas directamente sin necesidad de usar comandos o eventos. Para mostrar
esto, podemos considerar el siguiente cdigo:

module BlinkC {
uses interface Timer<TMilli> as Timer0;
uses interface Timer<TMilli> as Timer1;
uses interface Timer<TMilli> as Timer2;
uses interface Leds;
uses interface Boot;
}
implementation
{

void startTimers() {
call Timer0.startPeriodic( 250 );
call Timer1.startPeriodic( 500 );
call Timer2.startPeriodic( 1000 );
}

event void Boot.booted()


{
startTimers();
}

event void Timer0.fired()


{
call Leds.led0Toggle();
}

event void Timer1.fired()


{

73
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

call Leds.led1Toggle();
}

event void Timer2.fired()


{
call Leds.led2Toggle();
}
}
Al definir la funcin startTimers() dentro del propio componente podemos
llamarla directamente sin necesidad de usar la palabra clave call.

8 Operaciones Split-Phase.
En un sistema por bloqueo, cuando un programa invoca una funcin para
realizar una operacin que conlleva mucho tiempo, el control no retorna hasta que
la operacin se ha completado: el sistema se bloquea hasta que finaliza la
ejecucin. En nesC esto no ocurre, ya que realiza operaciones split-phase, es decir,
que cuando se requiere una operacin de larga duracin se devuelve el control
inmediatamente y cuando la operacin termina emite una seal. Veamos un
ejemplo que permite mostrar ambos tipos de sistemas:

BLOCKING SPLIT-PHASE

if (send() == SUCCESS) { // start phase


sendCount++; send();
}
//completion phase
void sendDone(error_t err) {
if (err == SUCCESS) {
sendCount++;
}
}
El cdigo para sistemas split-phase es algo ms complicado que el cdigo
secuencial, pero en cambio tiene muchas ventajas: las llamadas split-phase no
inhabilitan la capacidad de recepcin del sistema y reducen el tamao de la pila ya
que no necesitan tantas variables como un sistema secuencial.

Las interfaces split-phase permiten a un componente ejecutar varias


operaciones en paralelo y consumen menos memoria que las operaciones por
bloqueo, ya que no necesitan guardar el contexto de programacin al realizar la
llamada a la funcin.

Un ejemplo muy tpico, consistente en la realizacin de dos operaciones


separadas por un intervalo de tiempo. En ste se muestran las ventajas de un
sistema split-phase frente a uno por bloqueo:

74
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

BLOCKING SPLIT-PHASE

state = WAITING; state = WAITING;


op1(); op1();
sleep(500); call
op2(); Timer.startOneShot(500);
state = RUNNING;
event void Timer.fired() {
op2();
state = RUNNING;
}
En el mtodo por bloqueo, el sistema se detiene 500 milisegundos antes de
realizar la segunda operacin. Sin embargo, el sistema split-phase puede seguir
realizando otras operaciones ya que el timer genera un evento que ejecuta la
segunda operacin cuando haya transcurrido el tiempo necesario.

9 Comunicacin por el canal radio.


TinyOS proporciona un conjunto de interfaces para abstraer la
comunicacin entre motes. Estas interfaces usan para la comunicacin un tipo de
datos abstracto, implementado en una estructura denominada message_t.

La estructura de message_t est definida en /tos/types/message.h y es la


siguiente:

typedef nx_struct message_t {


nx_uint8_t header[sizeof(message_header_t)];
nx_uint8_t data[TOSH_DATA_LENGTH];
nx_uint8_t footer[sizeof(message_footer_t)];
nx_uint8_t metadata[sizeof(message_metadata_t)];
} message_t;
A los campos header, footer y metadata no puede accederse directamente,
tiene que hacerse a travs de interfaces habilitadas para ello.

9.1 Interfaces bsicas de comunicacin.


Veamos algunas de las interfaces que usan message_t como estructura de
datos para la comunicacin:

Packet: proporciona comandos para borrar el contenido de un mensaje,


conocer el tamao de la carga til o establecer un puntero a la misma.

Send: interfaz para enviar mensajes en difusin. Aporta comandos para


enviar un mensaje y cancelar el envo de un mensaje pendiente. Posee un
evento para indicar si el mensaje fue enviado o no con xito y contiene
funciones para calcular el tamao mximo de carga til. Tambin
proporciona un puntero a la zona de carga til.

75
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

Receive: interfaz bsica para recepcin de mensajes. Provee un evento


para indicar la recepcin de un mensaje y comandos para establecer un
puntero a la carga til y obtener su tamao.

9.2 Interfaces Active Message.

AMSend: proporciona la interfaz bsica de envo Active Message, similar a


send. La diferencia estriba en que aquel necesita una direccin AM para
identificar el destino. La direccin AM de un nodo es el identificador que se
asigna al programar el mote mediante make install,N o make
reinstall,N. Este identificador puede cambiarse en tiempo de ejecucin
con el componente ActiveMessageAddressC.

AMPacket: incluye comandos para obtener la direccin AM de un nodo, el


destino AM de un paquete y el tipo de paquete AM.

9.3 Componentes.
Existen varios componentes en la carpeta /tos/system que implementan las
interfaces bsicas de comunicacin y las Active Message. Algunos de estos
componentes son:

AMSenderC: implementa las interfaces AMSend, Packet, AMPacket y


PacketAcknowledgements como Acks.

AMReceiverC: implementa las interfaces Receive, Packet y AMPacket.

Debido a que TinyOS soporta mltiples arquitecturas, cada una de las cuales
tiene sus propios drivers, se emplea un componente llamado ActiveMessageC para
compatibilizar las interfaces antes expuestas con cada una de las arquitecturas. En
nuestro caso, que estamos trabajando con nodos micaz, el componente
ActiveMessageC necesario es implementado por CC2420ActiveMessage.

9.4 Modificacin de Blink para usar el canal radio.


Para ilustrar el uso de estas interfaces y componentes, vamos a disear una
aplicacin cuyo funcionamiento consiste en incrementar un contador, mostrar su
valor codificado en binario con los tres leds y enviar un mensaje con el valor del
contador por el canal radio.

En primer lugar, abrimos un nuevo directorio en la carpeta /apps en el que


guardaremos el programa que vamos a disear. A continuacin, creamos un
archivo llamado BlinkToRadioC.nc cuyo contenido es el siguiente:

#include <Timer.h>
#include "BlinkToRadio.h"

76
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

module BlinkToRadioC {
uses interface Boot;
uses interface Leds;
uses interface Timer<TMilli> as Timer0;
}
implementation {
uint16_t counter = 0;

event void Boot.booted() {


call Timer0.startPeriodic(TIMER_PERIOD_MILLI);
}

event void Timer0.fired() {


counter++;
call Leds.set(counter);
}
}
Si nos fijamos en el cdigo, veremos que en la primera lnea se encuentra
una directiva include. Esta directiva indica al compilador que busque el contenido
del archivo Timer.h en los sitios por defecto y considere su contenido parte del
programa. Los sitios por defecto son los subdirectorios de la carpeta /tos.

La segunda lnea es tambin una directiva include, con la diferencia de que


las comillas ordenan al compilador que busque el archivo en la carpeta actual antes
de buscar en los subdirectorios por defecto.

El contenido del archivo de cabecera BlinkToRadio.h es el siguiente:

#ifndef BLINKTORADIO_H
#define BLINKTORADIO_H

enum {
TIMER_PERIOD_MILLI = 250
};

#endif
Las directivas ifndef, define y endif se utilizan para asegurar que las
definiciones de cada archivo de cabecera no se incluyen ms de una vez. Un punto
a destacar es que las constantes se definen con el comando enum mejor que con
define, ya que aquel no reemplaza cada ocurrencia de la constante definida
independientemente de donde se encuentre en el cdigo.

Para ensamblar las interfaces usadas por unos componentes con las
provistas por otros se utiliza el componente BlinkToRadioAppC.nc, cuyo contenido
es el siguiente:

#include <Timer.h>
#include "BlinkToRadio.h"

77
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

configuration BlinkToRadioAppC {
}
implementation {
components MainC;
components LedsC;
components BlinkToRadioC as App;
components new TimerMilliC() as Timer0;

App.Boot -> MainC;


App.Leds -> LedsC;
App.Timer0 -> Timer0;
}
Ya slo resta crear un Makefile para poder compilar la aplicacin. Su
contenido es el siguiente:

COMPONENT=BlinkToRadioAppC
include $(MAKERULES)
La primera lnea de este Makefile, indica que el componente de
configuracin de alto nivel es BlinkToRadioAppC.nc y, la segunda, carga el sistema
de compilacin de TinyOS.

9.4.1 Definir la estructura del mensaje.

El siguiente paso es definir la estructura del mensaje que contiene la


informacin a enviar. Esta estructura debe incluir el identificador del nodo y el
valor del contador. En lugar de guardar y recuperar estos datos directamente
desde el mensaje message_t, definiremos una estructura donde almacenarlos y,
posteriormente, copiaremos la estructura en el rea de carga til del mensaje. El
uso de estructuras para almacenar datos en lugar de copiarlos sobre message_t
directamente, es muy til ya que evita el tener que usar de ndices para situar los
datos dentro de message_t correctamente.

Necesitamos definir una estructura que almacene dos nmeros enteros,


uno para el identificador y otro para el contador. Lo haremos de forma similar al
lenguaje C:

typedef nx_struct BlinkToRadioMsg {


nx_uint16_t nodeid;
nx_uint16_t counter;
} BlinkToRadioMsg;
El prefijo nx_ es especfico del lenguaje nesC y se usa para indicar que tanto
la estructura como las variables son tipos externos. Los tipos externos permiten
trabajar con la informacin sin tener que preocuparse de la arquitectura de la
plataforma con la que se est trabajando. El prefijo nx_ indica al compilador que se
est usando el formato big-endian, mientras que el prefijo nx_le hace referencia al
formato little-endian.

78
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

El compilador genera el cdigo de forma transparente al programador sin


que ste tenga que preocuparse del formato o del alineamiento de bits, de modo
que enviar los bits en serie por el canal radio no supone ningn problema en el
receptor a la hora de ordenarlos.

9.4.2 Envo del mensaje.


El envo de un mensaje por el canal radio se puede resumir en una serie de
pasos que describimos a continuacin:

1. Identificar las interfaces que proporcionan acceso al transceptor y permiten


manipular el tipo message_t.

Usaremos la interfaz AMSend para enviar los paquetes y las interfaces


Packet y AMPacket para acceder al tipo abstracto message_t. Aunque es posible
ensamblar directamente con el componente ActiveMessageC, usaremos AMSender
en su lugar. El motivo es que esta interfaz virtualiza el acceso radio y evita que dos
componentes que estn compartiendo el transceptor se interfieran entre s.
Adems, para inicializar el transceptor radio necesitamos la interfaz
ActiveMessageC.SplitControl.

2. Actualizar el bloque module en BlinkToRadioC.nc aadiendo las sentencias de


uso de las interfaces que necesitamos:

module BlinkToRadioC {
...
uses interface Packet;
uses interface AMPacket;
uses interface AMSend;
uses interface SplitControl as AMControl;
}
La interfaz SplitControl ha sido renombrada usando la palabra clave as. Esto
puede ser til para diferenciar en el caso de que se usen dos o ms componentes
en un mismo module que provean la misma interfaz o simplemente para trabajar
con un nombre con el que nos sea ms cmodo. En este caso, usamos AMControl
porque la interfaz SplitControl es la encargada de controlar el componente
ActiveMessageC.

3. Declarar las variables necesarias y aadir cualquier cdigo de inicializacin


necesario por las interfaces o componentes.

Vamos a necesitar un tipo message_t para la transmisin y una bandera


para indicar cuando el transmisor est ocupado. Estas declaraciones se realizan
dentro del bloque implementation en BlinkToRadio.nc:

implementation {

79
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

bool busy = FALSE;


message_t pkt;
...
}
A continuacin, queremos inicializar la radio cuando lo haga el equipo, por
lo que colocaremos la llamada AMControl.start dentro de Boot.booted.

Por otra parte, sera conveniente que el timer comenzase a contar una vez
que el sistema est inicializado y no al ejecutar la aplicacin, por lo que haremos la
llamada a la funcin del timer una vez que el sistema se haya inicializado:

event void AMControl.startDone(error_t err) {


if (err == SUCCESS) {
call Timer0.startPeriodic(TIMER_PERIOD_MILLI);
}
else {
call AMControl.start();
}
}
dejando el evento de inicializacin del sistema nicamente con la llamada a la
funcin de inicializacin del controlador de la interfaz radio:

event void Boot.booted() {


call AMControl.start();
}
El evento AMControl.startDone inicia el timer si la radio se ha inicializado
con xito (error_t = SUCCESS), en caso contrario, trata de inicializarla de nuevo.
Este proceso continuar hasta que se consiga iniciar la radio correctamente.

Como tenemos que implementar todos los eventos aunque no vayan a


utilizarse, el evento AMControl.stopDone quedara as:

event void AMControl.stopDone(error_t err) {


}

4. Invocar cualquier funcin de las interfaces necesarias en nuestra aplicacin.

En nuestro caso lo que queremos es que se enve el valor del contador y el


identificador del nodo cada vez que el timer expire, luego debemos de programar
lo siguiente:

event void Timer0.fired() {


...
if (!busy) {
BlinkToRadioMsg* btrpkt = (BlinkToRadioMsg*)(call
Packet.getPayload(&pkt, NULL));
btrpkt->nodeid = TOS_NODE_ID;
btrpkt->counter = counter;

80
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

if (call AMSend.send(AM_BROADCAST_ADDR, &pkt,


sizeof(BlinkToRadioMsg)) == SUCCESS) {
busy = TRUE;
}
}
}
En esta parte del cdigo se realizan muchas operaciones. Lo primero que se
hace es comprobar si el transmisor esta libre verificando la bandera. A
continuacin, se toma la parte correspondiente a la carga til del mensaje en
message_t y se realiza una conversin forzada (cast) a la estructura que hemos
definido, BlinkToRadioMsg. De esta forma podemos acceder al mensaje e iniciar los
valores de las variables usando el puntero btrpkt. Adems, podemos enviar el
mensaje a todos los nodos especificando la direccin AM_BROADCAST_ADDR
como direccin destino.

Finalmente, el sistema comprueba si la capa AM ha aceptado el mensaje


para transmitirlo y si es as, se cambia el estado de la bandera para indicar que el
transmisor est ocupado. Cabe resaltar que se podra haber ahorrado el uso de la
interfaz Packet ya que el comando getPayload tambin lo provee la interfaz
AMSend.

5. Implementar cualquier evento de las interfaces que planeemos usar.

Estudiando el cdigo de las interfaces Packet, AMPacket y AMSend se


observa que slo es necesario implementar un evento, AMSend.sendDone:

event void AMSend.sendDone(message_t* msg, error_t


error) {
if (&pkt == msg) {
busy = FALSE;
}
}
Este evento se genera despus de un intento de transmisin y se encarga de
cambiar el estado de la bandera. Se ha de verificar que el paquete que se ha
enviado es el mismo que el que coloc el componente en el buffer. Esto es as
porque si dos componentes ensamblan la misma interfaz AMSend, ambos
recibiran un evento sendDone si alguno de los dos hubiese llamado al comando
send.

81
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

6. Aadir al bloque implementation del archivo de configuracin cada uno de los


componentes que usan y proveen interfaces.

implementation {
...
components ActiveMessageC;
components new AMSenderC(AM_BLINKTORADIO);
...
}
Todas las interfaces necesarias en la aplicacin son provistas por estos dos
componentes. Existe una pequea diferencia en la declaracin de los
componentes: mientras que ActiveMessageC es un componente definido para cada
arquitectura, AMSender es un componente genrico. La palabra clave new indica
que una nueva instancia de AMSenderC va a ser creada y el parmetro
AM_BLINKTORADIO indica el tipo AM de AMSenderC. Este valor podemos indicarlo
en el bloque enum del archivo de cabecera BlinkToRadio.h:

enum {
AM_BLINKTORADIO = 6,
TIMER_PERIOD_MILLI = 250
};

7. Ensamblar las interfaces usadas en la aplicacin con los componentes que las
proveen.

Las lneas que establecen esta unin se sitan en el bloque implementation


de BlinkToRadioAppC.nc:

implementation {
...
App.Packet -> AMSenderC;
App.AMPacket -> AMSenderC;
App.AMSend -> AMSenderC;
App.AMControl -> ActiveMessageC;
}

9.4.3 Recepcin del mensaje.

Una vez desarrollada la parte de transmisin de los mensajes podemos


aadir el cdigo encargado de la recepcin de los mismos. De nuevo resumimos los
puntos a seguir:

1. Identificar las interfaces que proveen acceso al canal radio y permiten


manejar el tipo message_t.

En la aplicacin que estamos analizando usaremos la interfaz Receive para


recibir los paquetes.

82
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

2. Aadir las interfaces que necesitamos al bloque module en BlinkToRadioC.nc.

module BlinkToRadioC {
...
uses interface Receive;
}

3. Declarar variables y aadir cualquier cdigo de inicializacin que necesitemos.

No es necesario realizar nada en este punto en la aplicacin que estamos


considerando.

4. Invocar cualquier funcin de las interfaces necesarias en nuestra aplicacin.

No es necesario realizar nada en este punto en la aplicacin que estamos


considerando.

5. Implementar todos los eventos de las interfaces que hemos de usar.

Debemos implementar el evento Receive.receive :

event message_t* Receive.receive(message_t* msg, void*


payload, uint8_t len) {
if (len == sizeof(BlinkToRadioMsg)) {
BlinkToRadioMsg* btrpkt =
(BlinkToRadioMsg*)payload;
call Leds.set(btrpkt->counter);
}
return msg;
}
En primer lugar, comprobamos que el tamao del mensaje es el esperado. A
continuacin, la carga del mensaje sufre una conversin forzada a un puntero a
estructura del tipo BlinkToRadioMsg y asignado a una variable local para
posteriormente mostrar el valor del contador en los leds.

6. Aadir al bloque implementation del archivo de configuracin los


componentes que proveen y usan las interfaces de la aplicacin.

implementation {
...
components new AMReceiverC(AM_BLINKTORADIO);
...
}
Esta sentencia muestra que se crear una nueva instancia de AMReceiverC
y, adems, indica el tipo AM de AMReceiverC, que ha de ser el mismo que el
empleado en el transmisor en AMSenderC.

83
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

7. Ensamblar las interfaces usadas en la aplicacin con los componentes que las
proveen.

implementation {
...
App.Receive -> AMReceiverC;
}

10 Comunicacin de la red con el PC.


El objetivo de este punto es explicar los fundamentos bsicos de la
comunicacin entre la estacin base y el PC. Esto permitir recolectar datos de la
red, monitorizar el trfico y enviar comandos a los motes. Para la comunicacin con
la red se emplea en el PC una interfaz basada en Java.

10.1 Herramientas de comunicacin.


El nivel bsico de abstraccin para la comunicacin PC-gateway es un
paquete fuente. Un paquete fuente es un medio sobre el cual una aplicacin puede
enviar y recibir paquetes.

La mayora de las herramientas de comunicacin de TinyOS toman un


parmetro opcional que permite especificar el paquete fuente. Este parmetro
opcional se representa como comm. Por ejemplo, con un mote micaz como
estacin base, podramos monitorizar lo que se est recibiendo usando el siguiente
comando:

$ java net.tinyos.tools.Listen comm


serial@/dev/ttyUSB0:micaz
donde hemos supuesto que ttyUSB0 es el puerto serie virtual para programacin
(ver Captulo 4).

Para ver el funcionamiento de este comando podemos compilar e instalar la


aplicacin /apps/tests/TestSerial en un mote. Esta aplicacin enva un paquete al
puerto serie cada segundo y cuando lo recibe muestra el nmero de secuencia del
paquete en los leds.

Una vez instalado el programa en el mote, necesitamos ejecutar la


aplicacin Java que se ha generado durante la compilacin usando el siguiente
comando:

$ java TestSerial comm serial@/dev/ttyUSB1:micaz


donde hemos supuesto que ttyUSB1 es el puerto serie virtual para comunicacin.

El formato para definir el puerto fuente es el siguiente:

84
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

serial@<PORT>:<SPEED>
En lugar de especificar la velocidad numricamente, se puede indicar la
plataforma con la que se est trabajando y el sistema se encarga de convertirlo
automticamente. En el caso de la arquitectura micaz, la velocidad es de 57600
baudios. El archivo del sistema donde estn especificadas las conversiones
plataforma-velocidad es /support/sdk/java/net/tinyos/packet/BaudRate.java

Al ejecutar TestSerial la salida por pantalla debe ser similar a la siguiente:

Sending packet 1
Received packet sequence number 4
Sending packet 2
Received packet sequence number 5
Sending packet 3
Received packet sequence number 6
Sending packet 4
Received packet sequence number 7
Received packet sequence number 8
Sending packet 5
Received packet sequence number 9
Sending packet 6

10.2 Variable de entorno MOTECOM.


Existe la posibilidad de fijar la opcin comm y as se evita la necesidad de
escribirla en cada sentencia de ejecucin. Para fijar el parmetro comm se utiliza
una variable de entorno llamada MOTECOM. Si no especificamos la opcin comm
en la orden de ejecucin de un programa, el sistema buscar los parmetros del
puerto y la velocidad en la variable de entorno MOTECOM. Para fijar esta variable
de entorno introducimos la siguiente sentencia en la lnea de comandos:

$ export MOTECOM=serial@/dev/ttyUSB0:micaz

11 BaseStation y la herramienta Listen.


BaseStation es una utilidad de TinyOS que sirve para que la estacin base,
una vez programada con esta aplicacin, acte como puente entre el puerto serie y
la red de sensores. Es decir, cuando se reciba un paquete por el puerto serie, sta
lo retransmitir va radio y cuando el paquete se reciba por la interfaz radio en la
estacin base, sta lo reenviar por el puerto serie al PC.

Para comprobar su funcionamiento, tomamos uno de los dos nodos en los


que tenemos instalado BlinkToRadio e instalamos en l la aplicacin BaseStation,
situada en /apps/BaseStation (nodo 1). Cuando encendamos el nodo que an tiene
instalada la aplicacin BlinkToRadio (nodo 2), veremos que el nodo 1 conmuta el
led 0 cada vez que enve un paquete por la interfaz radio y el led 1 cada vez que lo
haga por el puerto serie. Asimismo, conmutar el led 2 cada vez que deba

85
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

descartar un paquete, por ejemplo, si se reciben mensajes a una tasa mayor de la


que la estacin base puede procesar.

A continuacin, podramos ver con la herramienta Listen lo que recibimos


por el puerto serie:

$ java net.tinyos.tools.Listen comm


serial@/dev/ttyUSB1:micaz
La salida por pantalla ser similar a la siguiente:

00 FF FF 00 00 04 22 06 00 02 00 01
00 FF FF 00 00 04 22 06 00 02 00 02
00 FF FF 00 00 04 22 06 00 02 00 03
00 FF FF 00 00 04 22 06 00 02 00 04
00 FF FF 00 00 04 22 06 00 02 00 05
00 FF FF 00 00 04 22 06 00 02 00 06
00 FF FF 00 00 04 22 06 00 02 00 07
00 FF FF 00 00 04 22 06 00 02 00 08
00 FF FF 00 00 04 22 06 00 02 00 09
00 FF FF 00 00 04 22 06 00 02 00 0A
00 FF FF 00 00 04 22 06 00 02 00 0B
Listen nicamente imprime los paquetes que se reciben del mote, que son
los mismos que se reenvan por el puerto serie. Recordar que la informacin se
enviaba en tipo message_t que contena la estructura que habamos definido para
almacenar el contador y el identificador del nodo. Por tanto, cada paquete que se
reciba contiene mltiples campos: el primer byte (00) indica que el paquete es del
tipo AM y el resto de bytes, a excepcin de los 4 ltimos, son campos genricos
Active Message definidos en /tinyos-2.1/tos/lib/serial/Serial.h.

Los 4 ltimos bytes son los valores del identificador del nodo y del contador.
Utiliza 2 bytes para cada variable porque en la definicin de la estructura
BlinkToRadioMsg asignamos un tamao de 16 bits a cada campo. Estos 4 ltimos
bytes constituyen la carga til del mensaje, que puede ser de hasta 28 bytes.

11.1 La utilidad MIG.


La herramienta Listen es el modo ms sencillo de monitorizar la informacin
recibida pero tiene la desventaja de que los datos los muestra en hexadecimal, lo
que convierte al mtodo en algo impracticable.

TinyOS posee otra herramienta que hace ms sencilla la recepcin y


visualizacin de datos de los motes: la herramienta mig. Con la herramienta mig se
puede crear una interfaz en Java, Python o C que construya un objeto a partir del
paquete recibido. Esto es, la utilidad mig analiza cada uno de los campos del
paquete recibido y provee una serie de utilidades para imprimir dichos campos.

86
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

TestSerial usa la herramienta mig para poder mostrar los paquetes recibidos
por el puerto serie. El modo de empleo de esta utilidad se puede observar en el
Makefile de TestSerial:

COMPONENT=TestSerialAppC
BUILD_EXTRA_DEPS += TestSerial.class
CLEAN_EXTRA = *.class TestSerialMsg.java

TestSerial.class: $(wildcard *.java) TestSerialMsg.java


javac *.java

TestSerialMsg.java:
mig java -target=null java
classname=TestSerialMsg TestSerial.h
TestSerialMsg -o $@

include $(MAKERULES)
La primera y la ltima lnea son las mismas que las de los makefiles de
aplicaciones sencillas en nesC que no requieren procesar informacin en el PC. En
estas aplicaciones nicamente incluimos el componente de alto nivel y las reglas de
compilacin (MAKERULES).

Con la lnea BUILD_EXTRA_DEPS indicamos al compilador que existe una


dependencia aadida en la aplicacin que debe de ser satisfecha antes de construir
el ejecutable, en este caso la dependencia es compilar la aplicacin TestSerial.class.

Con la lnea CLEAN_EXTRA, se indican los archivos que han de borrarse


cuando el usuario ejecuta el comando make clean.

La lnea:

TestSerial.class: $(wildcard *.java) TestSerialMsg.java


indica que TestSerial.class depende del archivo TestSerialMsg.java y de todos los
archivos de extensin .java que se encuentren en el mismo directorio que el
Makefile, mientras que:

javac *.java
muestra cmo obtener TestSerial.class a partir de las dependencias arriba
expuestas, es decir, compilando las dependencias.

En la siguiente lnea se muestra como obtener el archivo TestSerialMsg.java


empleando la herramienta mig. Veamos cada uno de los parmetros que recibe:

java: lenguaje en el que se desea que se genere el archivo.

87
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

-target=null: arquitectura con la que se est trabajando. Si se deja como


null, incluye todos los componentes por defecto del sistema, por lo que es
til usarla con la herramienta mig.

-java-classname=TestSerialMsg : el nombre del archivo java a generar.

TestSerial.h : lugar donde est definida la estructura.

TestSerialMsg : nombre de la estructura.

-o $@: nombre del archivo de salida, en este caso con $@ hacemos


referencia al archivo cuya regla estamos ejecutando, es decir,
TestSerialMsg.java.

Para utilizar la herramienta mig con la aplicacin que estamos diseando,


BlinkToRadio, deberamos aadir al Makefile las siguientes lneas de cdigo:

Las nuevas dependencias:

BUILD_EXTRA_DEPS=BlinkToRadio.class

Cmo resolver las dependencias:

BlinkToRadio.class: BlinkToRadioMsg.java
javac BlinkToRadioMsg.java
NOTA: Debe haber un tabulador justo antes de javac, no espacios.

Obtencin del archivo .java del que depende la clase:

BlinkToRadioMsg.java:
mig java -target=null -java-
classname=BlinkToRadioMsg BlinkToRadio.h
BlinkToRadioMsg -o $@

NOTA: Al igual que con javac debe haber un tabulador antes de mig y no
espacios.

Una vez hecho esto, tenemos que cambiar en BlinkToRadio.h el nombre del
tipo AM por AM_BLINKTORADIOMSG para evitar que se muestre un warning tras el
proceso de compilacin, ya que mig no puede identificar el tipo AM para
BlinkToRadioMsg. Tambin debemos cambiar este nombre en los argumentos que
reciben AMSenderC y AMReceiverC en el archivo BlinkToRadioAppC.nc

Para comprobar el funcionamiento de la aplicacin simplemente instalamos


la aplicacin BlinkToRadio en un mote y BaseStation en el que est conectado al PC

88
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC

actuando Gateway. Encendemos el mote y ejecutamos en la lnea de comandos lo


siguiente:

$ java net.tinyos.tools.MsgReader BlinkToRadioMsg comm


serial@/dev/ttyUSB1:micaz
as cuando la estacin base reenve un paquete al puerto serie, MsgReader lo lee e
imprime su contenido. La salida sera similar a la siguiente:

1152232617609: Message
[nodeid=0x2]
[counter=0x1049]

1152232617609: Message
[nodeid=0x2]
[counter=0x104a]

1152232617609: Message
[nodeid=0x2]
[counter=0x104b]

1152232617621: Message
[nodeid=0x2]
[counter=0x104c]

89

Anda mungkin juga menyukai