Contiki Kernel - Overview

Because of the characteristics of the platforms targeted by the Contiki OS, the RUNES Contiki kernel has a few limitations w.r.t. the C and Java versions. Specifically, multiple component instances are not supported, as well as the load() and unload() operations. Essentially, every RUNES component is automaticaly loaded and instantiated when it is deployed onto the node. Besides this, the RUNES component model and API are available as in the other platforms supported.

An Example

RUNES
Layers This section presents a toy example, and shows different ways of compiling and instantiating it. The example is a component-based calculator, as illustrated in the figure. A main component (calculator) delegates mathematical operations to other components. Our calculator only does addition. We have two adder components, one that is faulty and one that is working correctly. Note that functions should be static; similarly for variables the state of which needs to be maintained.

An interface is declared using DECLARE_INTERFACE. The arguments are the name of the interface and a collection of function pointers that represent the functions the interface offers. The adder interface would therefore look like this:

#ifndef __IADDER_H__
#define __IADDER_H__
#include "runes.h"
DECLARE_INTERFACE(iadder,
{
void (*add)(int a, int b, int *c);
})
#endif

Each component must have a header file, where it is defined. Here is the header file for the faulty adder:

#ifndef __FAULTYADDER_H__
#define __FAULTYADDER_H__
#include "runes.h"
DECLARE_COMPONENT(faultyadder,
{
void (*add)(int a, int b, int *c);
});
#endif

The only middleware specific thing here is the DECLARE_COMPONENT macro. This takes a component name and a list of functions this component implements as arguments. Similarly, here is the header file for the correctly working adder:

#ifndef __ADDER_H__
#define __ADDER_H__
#include "runes.h"
DECLARE_COMPONENT(adder,
{
void (*add)(int a, int b, int *c);
});
#endif

This is the implementation of the faulty adder, which does multipltication instead of addition (hence it is faulty):

#include
#include "faultyadder.h"
static void construct() {
   printf("Faulty Adder Instantiated..\n");
}
static void add(int a, int b, int *c) {
   *c=a*b;
}
static void destroy() {
   printf("Faulty Adder Going Away...\n");
}
IMPLEMENT_COMPONENT(faultyadder,{add});

Here the RUNES-specific constructs are the following construct()/destroy() and IMPLEMENT COMPONENT. The former are the default constructor and destructor of the component. Note that if you do not implement these, your component will not compile. The latter is a macro that defines a component implementation. The parameters show that it implements faultyadder (see the header file above) and includes a list of methods that are implemented (note that these should be in the same order as defined in the interface). Similarly, in the following we illustrate the implementation of the correctly functioning adder component:

#include
#include "adder.h"
static void construct() {
   printf("Adder Instantiated..\n");
}
static void add(int a, int b, int *c) {
   *c=a+b;
}
static void destroy() {
   printf("Adder Going Away...\n");
}
IMPLEMENT_COMPONENT(adder,{add});
What follows instead is a description of the calculator implementation. Note that there are a few of Contiki-specific constructs here. Those will not be discussed in any great detail. Note that the component includes a process, called rebinding, that maps the user button on the sensor. It also blinks the red LED when it's calculating. This process turns on the blue LED when the calculator is bound to the correctly working adder.

#include "calculator.h"
#include "lib/sensors.h"
#include "dev/button-sensor.h"
#include "dev/leds.h"
#include "dev/light.h"
#include "crtk.h"
static Receptacle adder_r;
PROCESS(button_process, "Rebinding");
PROCESS(calculating_process, "Our Calculator");
static void construct() {
   INVOKE("crtk",crtk,connect("faultyadder",&adder_r));
   process_start(&button_process,NULL);
   process_start(&calculating_process,NULL);
   printf("Calculator Instantiated...\n");
}
static void add(int a, int b) {
   int result;
   INVOKE(adder_r,adder,add(a,b,&result));
   printf("%d + %d = %d\n",a,b,result);
}
static void destroy() {
   printf("Calculator Going Away...\n");
}

PROCESS_THREAD(calculating_process,ev,data) {
   static struct etimer timer;
   PROCESS_EXITHANDLER(goto exit3);
   PROCESS_BEGIN();
   while(1) {
     etimer_set(&timer, CLOCK_SECOND);
     PROCESS_YIELD_UNTIL(etimer_expired(&timer));
     leds_toggle(LEDS_RED);
     add(10,5);
   }
   PROCESS_YIELD_UNTIL(ev == PROCESS_EVENT_SERVICE_REMOVED);
  exit3:
   PROCESS_END();
}

PROCESS_THREAD(button_process,ev,data) {
   PROCESS_EXITHANDLER(goto exit2);
   PROCESS_BEGIN();
   button_sensor.activate();
   sensors_select(&button_sensor, &button_process);
   while(1) {
     PROCESS_WAIT_EVENT();
     if(ev == sensors_event && data == &button_sensor) {
       if(strcmp(adder_r,"faultyadder")==0) {
         INVOKE("crtk",crtk,connect("adder",&adder_r));
       } else {
         INVOKE("crtk",crtk,connect("faultyadder",&adder_r));
       }
       leds_toggle(LEDS_BLUE);
     }
   }
  exit2:
   PROCESS_END();
}
IMPLEMENT_COMPONENT(calculator,{add});

Points of interest in the code shown above are: