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.
58
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC
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
Instalacin en el mote:
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:
Documentacin de la aplicacin:
$ 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
configuration BlinkAppC {
}
implementation
{
components MainC, BlinkC, LedsC;
components new TimerMilliC() as Timer0;
components new TimerMilliC() as Timer1;
components new TimerMilliC() as Timer2;
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.
63
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC
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:
y
BlinkC.Timer0 -> Timer0;
64
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC
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 );
}
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.
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.
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;
67
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC
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.
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;
68
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC
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();
interface Leds {
/**
* Turn LED X on, off, or toggle its present state.
*/
async command void led0On();
async command void led0Off();
async command void led0Toggle();
/**
* 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
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.
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.
71
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC
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.
72
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC
uint32_t i;
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 );
}
73
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC
call Leds.led1Toggle();
}
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
74
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC
BLOCKING SPLIT-PHASE
75
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC
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:
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.
#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;
#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;
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.
78
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC
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.
implementation {
79
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC
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:
80
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC
81
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC
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.
implementation {
...
App.Packet -> AMSenderC;
App.AMPacket -> AMSenderC;
App.AMSend -> AMSenderC;
App.AMControl -> ActiveMessageC;
}
82
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC
module BlinkToRadioC {
...
uses interface Receive;
}
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;
}
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
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
$ export MOTECOM=serial@/dev/ttyUSB0:micaz
85
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC
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.
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
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).
La lnea:
javac *.java
muestra cmo obtener TestSerial.class a partir de las dependencias arriba
expuestas, es decir, compilando las dependencias.
87
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC
BUILD_EXTRA_DEPS=BlinkToRadio.class
BlinkToRadio.class: BlinkToRadioMsg.java
javac BlinkToRadioMsg.java
NOTA: Debe haber un tabulador justo antes de javac, no espacios.
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
88
ANEXO C. INTRODUCCIN A LA PROGRAMACIN EN TINYOS: NESC
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