Contiki - Understanding the code

From Cyber-Physical Systems Laboratory
Jump to navigationJump to search

Contiki description

The Contiki folder contains various directories. Here are the ones you will be interested in if you want to develop your Contiki app:

  • app: contains apps you can include to you project and use in your code
  • core: contains the code part of ContikiOS (net stack, serial, radio, process, timers...)
  • cpu: contains the code related to each unique CPU
  • platform: contains the code related to the platform you use (2 different platforms can share the same CPU)
  • lib: some extra libraries
  • examples: a list of interesting examples of how to use most of the functionalities and apps available on ContikiOS


Contiki code

Protothreads

ContikiOS runs with protothreads, an event based system with non blocking functions. Indeed if a function comes to block the system a watchdog will force the reset of the platform.

Create a protothread

To create a protothread it's easy:

  • Create the variable:
PROCESS(MyProcess, "Optional name that can be ignored at compilation time (saves ROM memory)");
  • Create the thread:
PROCESS_THREAD(MyProcess, ev, data)
{
    PROCESS_BEGIN();

    // Your code

    PROCESS_END();
}
  • Additionally you can add it to a header file for external access
PROCESS_NAME(MyProcess);

Some rules must be known: do not use switch into a protothread; some times you can have unexpected results.

Do not block a protothread more than a few milliseconds (check the documentation for more details).

Working with a protothread

Now that we have the skeleton of our protothread we need to be able to work with it.

You must know that a protothread is a function that exits at some point to continue working later. If you want to save variables from one event to another you must declare static variables:

PROCESS_THREAD(MyProcess, ev, data)
{
    // Declare your static variables
    static int a;

    PROCESS_BEGIN();
    // Initialize them AFTER PROCESS_BEGIN();
    // (you can also declare them static here)
    a = 0; 


    PROCESS_END();
}

Next step is to continue using this PROCESS as long as we need it. For that, usually, the PROCESS contains a while(1) loop.

But in order not to block the PROCESS we need to add one of the following functions:

  • PROCESS_WAIT_EVENT(): waits until ANY event occurs, you can then manually sort which event it was with the ev variable.
  • PROCESS_WAIT_EVENT_UNTIL(condition): waits until THIS condition is true.
  • PROCESS_PAUSE(): pauses the process and emits a PAUSE event to continue at this point after the execution of other processes.
  • PROCESS_WAIT_UNTIL(event): wait until event is triggered.
  • PROCESS_WAIT_WHILE(event): wait while event is triggered.

There are some other functions you might want to use such as:

  • PROCESS_EXIT() to exit the current running process
  • process_post(struct process *p, process_event_t ev, process_data_t data): to post a asynchronous event (the process will wait its turn)
  • process_post_synch(struct process *p, process_event_t ev, process_data_t data): to post a synchronous event (the process will be immediately called)

Simply speaking our code looks like this now:

PROCESS_THREAD(MyProcess, ev, data)
{
    // Declare your static variables
    static int a;

    PROCESS_BEGIN();
    // Initialize them AFTER PROCESS_BEGIN();
    // (you can also declare them static here)
    a = 0; 

    while(1)
    {
        PROCESS_WAIT_EVENT();
    }


    PROCESS_END();
}

Now to go further we want to create a hello world PROCESS that will print "Hello world %d!" with %d being the variable a incremented each second.

We will create an event timer that will trigger our function.

To wait for the timer we have two choices (only if we use 1 timer):

  • PROCESS_WAIT_UNTIL(PROCESS_EVENT_TIMER) we wait until a timer event is triggered.
  • PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&periodic_timer)) we wait until this timer has expired.

Let's choose the second option as it is the best one in case you manipulate multiple etimers in the same PROCESS.

PROCESS(MyProcess);

PROCESS_THREAD(MyProcess, ev, data)
{
    // Declare your static variables
    static int a;
    static struct etimer periodic_timer;

    PROCESS_BEGIN();
    // Initialize them AFTER PROCESS_BEGIN();
    // (you can also declare them static here)
    a = 0;

    // CLOCK_SECONDS gives the number of counts to get to 1s. THOSE ARE NOT MILLISECONDS (e.g. some are 128 tics / second)
    etimer_set(&periodic_timer, CLOCK_SECOND*1);

    while(1)
    {
        PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&periodic_timer));
        // And we don't forget to reset the timer:
        etimer_reset(&periodic_timer);

        printf("Hello world %d!", a++);
    }


    PROCESS_END();
}

Advanced use

Many more functions and possibilities are available. For that you may want to take a look at:

  • The documentating (you should generate your own doxygen documentation using the contiki folder)
  • Taking a look at the files in core/sys (process.h for the protothreads)