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
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
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
#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});
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:
#include "crtk.h"
We use the CRTK to do rebinding, so we need to include it's header file.-
static Receptacle adder r;
is the way we declare a receptacle. PROCESS(button process, "Rebinding");
We declare a process called rebinding. This is not related to the Runes Middleware, but is a Contiki macro. This process will monitor the button and rebind the calculator.INVOKE("crtk",crtk,connect("faultyadder",&adder r));
this is how receptacles are used to make calls to functions in other components.crtk
is the component name (the CRTK) or receptacle, crtk is the interface, and conenct is the function name. This invocation essentially uses the CRTK to bind the calculator receptacle to the faulty adder.INVOKE(adder_r,adder,add(a,b,&result));
invokes the add() operation, using theadder
interface on theadder_r
receptacle.- The
button
process. This process initialises the button sensor and monitors it for events. If the button it pressed, it uses the INVOKE operation to rebind the receptacle.