Tiny OS Networked Sensor Device (Mote) Programming

***In progress ***


A TinyOS networked sensor device application is described by a graph of software components, the lowest of which simply encapsulate the physical hardware.  Components have state, contained in their TOS frame, an interface, specifying what commands it implements, what events it signals, what commands it uses, and what events it handles, and its internal logic, including the functions implementing its interface and additional threads.  The combination of interface-based composition and internal concurrency and state provides a smooth intermixing of hardware and software functionality.

There are three aspects of programming networked sensor devices using TinyOS:

Composing components into an application

A complete application is described by a graph of components.  A typical component has an 'upper-interface', consisting of the commands that it implements (or accepts) and the events that it signals, and a 'lower-interface', consisting of the commands that it uses and events that it handles.   Lowest-level components provide only an upper-interface, as they are implemented directly by physical hardware modules.  They typically describe their mapping to specific hardware resource, in lieu of a lower interface. The other exception is the MAIN component, which has only a lower interface providing the initialization commands into the application.

Composing an application involves specifying a collection of components and 'wiring ' the command and event relationships between components.  TOS currently provides a set of tools that allow component composition to be described using textual editors.  We also have support for using schmatic editing tools such as Workview Viewlogic to produce structural VHDL (only a small subset is used).  

You should start with a working .desc file, such as those contained in the apps directory.  The .desc file has two parts

The sequel describes what is in a basic component graph and a graph using communication components.
 

Components

Components have two files that describe them 1)A .comp file which contains the interface specification for the component. 2)A .c file that contains the implementation of the component.  The interface description contains 5 parts.

  1.  list of commands it ACCEPTS

  2.  list of events it SIGNALS

  3.  list of commands it USES

  4.  list of events it HANDLES

  5. list of INTERNAL methods – not used to compose components but automatically placed in header files

Here is an example of  a a component description file.

/*AM.comp*/
ACCEPTS{
char AM_SEND_MSG(char addr,char type, char* data);
char AM_POWER(char mode);
char AM_INIT();
};


SIGNALS{
char AM_MSG_REC(char type, char* data);
char AM_MSG_SEND_DONE(char success);
};

HANDLES{
char AM_TX_PACKET_DONE(char success);
char AM_RX_PACKET_DONE(char* packet);
};

USES{
char AM_SUB_TX_PACKET(char* data);
void AM_SUB_POWER(char mode);
char AM_SUB_INIT();
};

Basic Component Graph

The following is drawn from the example apps/blink.vhd

The list of components is simple:

include modules{
MAIN;
BLINK;
CLOCK;
LEDS;
};

The body shows how the ports are all wired together.  The names don't matter, but it sure helps to make them meaningful.  Here we see that the MAIN_SUB_INIT is wired to BLINK_INIT.  BLINK_SUB_INIT is wired to CLOCK_INIT.  If there is more than one IN port on a net, the tools will create the fan-out function automatically.

BLINK:BLINK_INIT MAIN:MAIN_SUB_INIT
BLINK:BLINK_APP_DONE MAIN:MAIN_SUB_SEND_DONE
BLINK:BLINK_LEDy_on LEDS:YELLOW_LED_ON
BLINK:BLINK_LEDy_off LEDS:YELLOW_LED_OFF
BLINK:BLINK_LEDr_on LEDS:RED_LED_ON
BLINK:BLINK_LEDr_off LEDS:RED_LED_OFF
BLINK:BLINK_LEDg_on LEDS:GREEN_LED_ON
BLINK:BLINK_LEDg_off LEDS:GREEN_LED_OFF
BLINK:BLINK_SUB_INIT CLOCK:CLOCK_INIT
BLINK:BLINK_CLOCK_EVENT CLOCK:CLOCK_FIRE_EVENT

Most lower-level components provide an initialization command and a power management command.

You may have noticed an extra signal, AM_DISP.  It is magic and described below.   If you would rather follow the simple BLINK example through, skip to Building a Application Component.
 

Graph with Communication Components:

Application components that do communication will provide active message handlers.  Currently, in order for the tools to manage the handler registration and dispatching, you need to follow some rather kludgy conventions.  The component port should be of the name AM_MSG_HANDLER_x, where x is a number.  Note capitalization.  No two handlers in the application should use the same number.  The signal name that is used is AM_DISP(x) - same x.   The component file provides a function AM_msg_handler_x (note change in case), that takes a point to the inbound message buffer as argument.

These applications also needs a stack of components stretching down from AM to the physical media.  The AM component is generic to a wide range of applications, so you will seldom need to change it. In order to assist with the repetitive use of the same set of components, we have the ability to specify complex components that are an assembly of basic components.  One such component is the GENERIC_COMM component which contains a complete RFM radio stack from the Active Messages layer down to the bit processing layer.

A high-level application component will typically implement the upper initialization commands, forward them to lower components as appropriate and then operate in an event driven mode, as defined by the collection of event handlers.  The rules are these.  Component FOO is described by FOO.c.  The declarations for each of the ports appear in the associated .h file and the associated functions appear in the C file.  The names used are the port names.  The C file will provide a function for every IN port.  It will invoke functions associated with OUT ports, using the port name.  Commands and events return control information and possibly data.  0 is treated as failure.  Non-zero indicates success.  This is discussed more with respect to event-based data pumps.

A component should not need to name functions in other components.  If it does, the modularity is broken.  The tools will translate internal names to external names, as specified by the .desc file.

Application Component

For the simple BLINK example above, we have the associated include file BLINK.h containing:

#ifndef __blinkHEAD__
#define __blinkHEAD__

#include "tos.h"
#include "blink.h"

//ACCEPTS:
char TOS_COMMAND(BLINK_INIT)();
char TOS_COMMAND(BLINK_START)();

//SIGNALS:

//HANDLES
void BLINK_CLOCK_EVENT();

//USES
char TOS_COMMAND(BLINK_SUB_INIT)(char interval);

#endif

It must include the tos.h header file.

PC based TINYOS emulation.

MakefilePC provides the hooks for building a emulation version of the NSD application for debugging purposes.  It even emulates the radio.  In the future, this support will be incoporated more transparently into lower layers.

It builds up some handy utilty functions, such as:

void LEDy_on()
{
#ifdef FULLPC
  printf("Y");
#else
  CLR_BLINK_LED1();
#endif
}

Memory Model -- the FRAME

It declares a frame to hold its internal state.  Variable state is only visible within this component and referenced as VAR(state).
 

#define TOS_FRAME_TYPE BLINK_frame
TOS_FRAME_BEGIN(BLINK_frame) {
        char state;
}
TOS_FRAME_END(BLINK_frame);

The implementation of the BLINK_INIT turns on all the LEDs and in turn initializes the clock component.  Notice, it refers to the internal name BLINK_SUB_INIT, which is bound to CLOCK_INIT through the vBINIT signal.

char TOS_COMMAND(BLINK_INIT)(){
  LEDy_on();   LEDr_on();   LEDg_on();
  TOS_COMMAND(BLINK_SUB_INIT)(0x03);         /* initialize clock component */
  VAR(state) = 0;
  return 1;
}

The BLINK_START command, which is invoked after initialization completes, turns all the LEDs off and completes, returning success.

char TOS_COMMAND(BLINK_START)(){
  LEDy_off();  LEDr_off();  LEDg_off();
  return 1;
}

The TOS scheduler puts the hardware into sleep mode when there is no pending tasks.  It remains sensitive to interrupts, such as the real-time clock.  BLINK handles this clock event.  It increments a 3-bit counter and sets the LEDs accordingly.

/* Clock Event Handler:
   update LED state as 3-bit counter and set LEDs to match
 */
void BLINK_CLOCK_EVENT(){
  char state = VAR(state) = (VAR(state)+1) & 7;
  if (state & 1) LEDy_on();  else LEDy_off();
  if (state & 2) LEDg_on();  else LEDg_off();
  if (state & 4) LEDr_on();  else LEDr_off();

The BLINK compenent shows the command/event driven control aspect of Tiny OS and the component modularity.  More sophisticated components demonstrate the use of the command/event protocol for data pumps for streaming data across one or more component layers and the use of TOS threads to provide internal concurrency.   In the high-level application component, these capabilities are project up simply as Active Message operations: send command and arrival handler.  These are illustrated by a simple example CHIRP that periodically takes a sensor reading and broadcasts it on the network.
 
 

Developing a low-level application component


Event-based Data Pumps

Internal concurrency