DOC

Hello World

By Esther Flores,2014-10-25 22:53
6 views 0
Hello World

Hello World

    The 01_hello-world example implements two concurrent processes. The first is called “hello_world” and will periodically print “Hello World” and an increasing counter on the serial port. The second is called “blink” and will blink the LEDs at a higher rate than the other process.

    The source files for this example is found in the example/01_hello-world folder. The hello-world.c file is the application main file, it contains all the application specific code and the Makefile indicates how to build the application.

    Here is the source code of the application:

     1: #include "contiki.h"

     2: #include "dev/leds.h"

     3:

     4: #include /* For printf() */

     5: /*-------------------------------------------------------------*/

     6: /* We declare the two processes */

     7: PROCESS(hello_world_process, "Hello world process");

     8: PROCESS(blink_process, "LED blink process");

     9:

    10: /* We require the processes to be started automatically */ 11: AUTOSTART_PROCESSES(&hello_world_process, &blink_process);

    12: /*-----------------------------------------------------------*/ 13: /* Implementation of the first process */

    14: PROCESS_THREAD(hello_world_process, ev, data)

    15: {

    16: // variables are declared static to ensure their values are kept 17: // between kernel calls.

    18: static struct etimer timer;

    19: static int count = 0;

    20:

    21: // any process must start with this.

    22: PROCESS_BEGIN();

    23:

    24: // set the etimer module to generate an event in one second.

    25: etimer_set(&timer, CLOCK_CONF_SECOND);

    26: while (1)

    27: {

    28: // wait here for an event to happen

    29: PROCESS_WAIT_EVENT();

    30:

    31: // if the event is the timer event as expected...

32: if(ev == PROCESS_EVENT_TIMER)

    33: {

    34: // do the process work

    35: printf("Hello, world #%i\n", count);

    36: count ++;

    37:

    38: // reset the timer so it will generate an other event

    39: // the exact same time after it expired (periodicity

    guaranteed)

    40: etimer_reset(&timer);

    41: }

    42:

    43: // and loop

    44: }

    45: // any process must end with this, even if it is never reached.

    46: PROCESS_END();

    47: }

    48: /*------------------------------------------------------------*/

    49: /* Implementation of the second process */

    50: PROCESS_THREAD(blink_process, ev, data) 51: {

    52: static struct etimer timer;

    53: static uint8_t leds_state = 0;

    54: PROCESS_BEGIN();

    55:

    56: while (1)

    57: {

    58: // we set the timer from here every time 59: etimer_set(&timer, CLOCK_CONF_SECOND / 4); 60:

    61: // and wait until the vent we receive is the one we're waiting

    for

    62: PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER); 63:

    64: // update the LEDs

    65: leds_off(0xFF);

    66: leds_on(leds_state);

    67: leds_state += 1;

    68: }

    69: PROCESS_END();

    70: }

    71: /*-------------------------------------------------------*/

    hello-world.c

    The code is rather fully documented, but let's see once more how processes

    are made:

    ; First the process must be declared using the macro: PROCESS(name,

    strname). The arguments name and strname are the variable name and

    the process described name respectively. You will refer to the

    process using the process name.

    ; The process definition is a function whose definition is the macro:

    PROCESS_THREAD(name, ev, data). The name argument should be

    replaced by the process name specified in the PROCESS macro. ev and

    data should be let as they are, those will be variables that will

    indicate what event generated the process execution (ev) and a

    pointer to optional data passed as the event was generated (data). ; The process function may declare some variables, then must start

    with the macro PROCESS_BEGIN() and end with PROCESS_END(). These

    two macro work together, as they in-fact implement a

    switch-statement. The code placed before PROCESS_BEGIN() will be

    executed every time the process is executed (or scheduled), which

    is most likely not wanted since the process is supposed to continue

    from where it blocked. Between these two macros is the application

    user code.

    ; The process code once executed will run until a specific macro is

    reached indicating a wait for an event. Here are some of the blocking

    macro calls you can make:

    o PROCESS_WAIT_EVENT(): yield and wait for an event to be

    posted to this process;

    o PROCESS_WAIT_EVENT_UNTIL(cond): yield and wait for an event,

    and for the cond condition to be true;

    o PROCESS_WAIT_UNTIL(cond): wait until the cond condition is

    true. If it is true at the instant of call, does not yield;

    o PROCESS_WAIT_WHILE(cond): wait while the cond condition is

    true. If it is false at the instant of call, does not yield;

    o PROCESS_PAUSE(): post an event to itself and yield. This

    allows the kernel to execute other processes if there are

    events pending, and ensures continuation of this process

    afterward;

    o PROCESS_EXIT(): terminate the process.

    Edit

     Event Post

    This second example called 02_event-post implements two processes: the

    first reads a temperature sensor periodically, and averages the measures

    over a given number of samples. When a new measure is ready, this process posts an event to another process in charge of printing the result over a serial link. Not only the event is posted (which is a notification), but a pointer to the measured data is passed to the other process as well. The source files for this example are located in the

    examples/02_event-post folder. The main source file is event-post.c. Here is the code for this example:

     1: #include "contiki.h"

     2: #include "dev/leds.h"

     3:

     4: #include /* For printf() */

     5:

     6: /* Driver Include */

     7: #include "ds1722.h"

     8:

     9: /* Variables: the application specific event value */

     10: static process_event_t event_data_ready;

     11:

     12: /*----------------------------------------------------------*/

     13: /* We declare the two processes */

     14: PROCESS(temp_process, "Temperature process");

     15: PROCESS(print_process, "Print process");

     16:

     17: /* We require the processes to be started automatically */

     18: AUTOSTART_PROCESSES(&temp_process, &print_process);

    19:/*----------------------------------------------------------*/

     20: /* Implementation of the first process */

     21: PROCESS_THREAD(temp_process, ev, data)

     22: {

     23: // variables are declared static to ensure their values are kept

     24: // between kernel calls.

     25: static struct etimer timer;

     26: static int count = 0;

     27: static int average, valid_measure;

     28:

     29: // those 3 variables are recomputed at every run, therefore it is not

     30: // necessary to declare them static.

     31: int measure;

     32: uint8_t msb, lsb;

     33:

     34: // any process mustt start with this.

     35: PROCESS_BEGIN();

     36:

     37: /* allocate the required event */

     38: event_data_ready = process_alloc_event();

     39:

     40: /* Initialize the temperature sensor */

     41: ds1722_init();

     42: ds1722_set_res(10);

     43: ds1722_sample_cont();

     44:

     45: average = 0;

     46:

     47: // set the etimer module to generate an event in one second.

     48: etimer_set(&timer, CLOCK_CONF_SECOND/4);

     49:

     50: while (1)

     51: {

     52: // wait here for the timer to expire

     53: PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER);

     54:

     55: leds_toggle(LEDS_BLUE);

     56:

     57: // do the process work

     58: msb = ds1722_read_MSB();

     59: lsb = ds1722_read_LSB();

     60:

     61: measure = ((uint16_t)msb) << 2;

     62: measure += (lsb >> 6) & 0x03;

     63:

     64: average += measure;

     65: count ++;

     66:

     67: if (count == 4)

     68: {

     69: // average the sum and store

     70: valid_measure = average >> 2;

     71:

     72: // reset variables

     73: average = 0;

     74: count = 0;

     75:

     76: // post an event to the print process

     77: // and pass a pointer to the last measure as data

     78: process_post(&print_process, event_data_ready, &valid_measure);

     79: }

     80:

     81: // reset the timer so it will generate another event

     82: etimer_reset(&timer);

     83: }

     84: // any process must end with this, even if it is never reached.

     85: PROCESS_END();

     86: }

     87:

    /*----------------------------------------------------------------*/

     88: /* Implementation of the second process */

     89: PROCESS_THREAD(print_process, ev, data)

     90: {

     91: PROCESS_BEGIN();

     92:

     93: while (1)

     94: {

     95: // wait until we get a data_ready event

     96: PROCESS_WAIT_EVENT_UNTIL(ev == event_data_ready);

     97:

     98: // display it

     99: printf("temperature = %u.%u\n", (*(int*)data)>>2, ((*(int*)data)&0x3)*25);

    100:

    101: }

    102: PROCESS_END();

    103: }

    104:

    /*-----------------------------------------------------------------*/ event-post.c

    The main difference with the previous example is that the two processes interact with each other. This is achieved thanks to events, which allow waking up processes while indicating them why they have been woken and optional data is passed to them.

    There is a certain number of system-defined events, here is the list:

    ; PROCESS_EVENT_NONE

    ; PROCESS_EVENT_INIT

    ; PROCESS_EVENT_POLL

    ; PROCESS_EVENT_EXIT

; PROCESS_EVENT_SERVICE_REMOVED

    ; PROCESS_EVENT_CONTINUE

    ; PROCESS_EVENT_MSG

    ; PROCESS_EVENT_EXITED

    ; PROCESS_EVENT_TIMER

    ; PROCESS_EVENT_COM

    ; PROCESS_EVENT_MAX

    These events are used by the kernel internally, but may be used in an

    application with care. Otherwise new events can be defined using the

    following function:

    ; process_event_t process_alloc_event (void)

    Hence declaring a global variable that will receive the new event value

    can then be used amongst processes, as it is done in the example.

    Process interaction is achieved with the following functions: ; void process_start (struct process *p, const char *arg): this adds

    a process to the kernel active process list. Processes may be

    started automatically using the AUTOSTART_PROCESS macro (see the

    examples for how to use it), or started manually using this function.

    The argument p points to the process variable (declared with the

    PROCESS macro), and arg is the data the process will receive for

    its initialization call.

    ; void process_exit (struct process *p): this stops a process and

    removes it from the kernel active process list. What is really done

    is: all the other processes will receive an event informing them

    the process is about to exit, then the process itself is run a last

    time with the event 'PROCESS_EVENT_EXIT' to let it end correctly,

    and finally the process is removed from the kernel list.

    ; int process_post (struct process *p, process_event_t ev, void

    *data): this function is the most generic, it posts an event to a

    given process, specifying the event type and a pointer to the data

    passed. The value returned is PROCESS_ERR_OK if the event is posted,

    or PROCESS_ERR_FULL if the event queue was full and the event could

    not be posted. If the event is posted, the kernel will execute the

    process when it will have done so with all the other processes with

    an event pending.

    ; void process_post_synch (struct process *p, process_event_t ev,

    void *data): this function is similar to the previous one, except

    that if the event can be posted the process will execute now with

    the event. In other word, it will skip the event waiting queue, and

    when it returns, the calling process will continue its execution

    until the next blocking macro call.

    ; void process_poll (struct process *p): this last function sets the

    process to be requiring a poll. A poll request is a little different

    from a event post, because before processing an event, the kernel

    first processes all the poll request for all the running processes.

    When it does, the polled process is executed with a

    PROCESS_EVENT_POLL event and no data.

    Edit

    Echo Server (TCP)

    This third example brings several new features at once. We'll see here how to set up the uIP stack (IPv4 only for now), how to set up SLIP (Serial Line IP) for enabling IP connectivity between a PC running Linux and a WSN430 with a serial link, and how to implement a simple TCP echo server using the protosocket library.

    This example is found in the example/03_echo-server folder, and contains the following files: echo-server.c for the echo server code, and the Makefile to build the project. Let's get started„

    Edit

     uIP setup

    In order to enable uIP, adding CFLAGS += -DWITH_UIP=1 in the Makefile file of the project folder is all what's required. But for your information, let's see what is done to make uIP functional. The networking setup is executed in the platform/wsn430/contiki-wsn430-init-net.c file.

    ; initialize the modules: the uip and uip_fw modules must be

    initialized by starting their processes:

     process_start(&tcpip_process, NULL);

     process_start(&uip_fw_process, NULL);

    ; set the node IP address: the node IP address is set according to

    its Rime address. The Rime module is initialized before, and its

    address is obtained from the DS2411 unique identifier value. The

    subnetwork address is hardcoded to 172.16.0.0/16, and the two lower

    bytes only are picked from the Rime address:

     uip_ipaddr_t hostaddr, netmask;

     uip_init();

     uip_ipaddr(&hostaddr, 172,16,

     rimeaddr_node_addr.u8[0],rimeaddr_node_addr.u8[1]);

     uip_ipaddr(&netmask, 255,255,0,0);

     uip_ipaddr_copy(&meshif.ipaddr, &hostaddr);

     uip_sethostaddr(&hostaddr);

     uip_setnetmask(&netmask);

     uip_over_mesh_set_net(&hostaddr, &netmask);

    ; define some network interfaces: the network interfaces are bound

    to subnetworks. A uIP network interface contains a subnetwork IP

    address, a netmask an a function used to send the IP packet. When

    using the radio interface to send IP packets, the Rime module is

    used. In order to send IP packet to a gateway, the SLIP is used to

    send them on a serial link. ATherefore the network interfaces are

    defined as follows:

     static struct uip_fw_netif meshif =

     {UIP_FW_NETIF(172,16,1,0, 255,255,0,0, uip_over_mesh_send)};

     static struct uip_fw_netif slipif =

     {UIP_FW_NETIF(0,0,0,0, 0,0,0,0, slip_send)};

    ; register the interfaces: once the network interfaces are defined

    as described above, they must be registered to the uip module:

     uip_over_mesh_set_gateway_netif(&slipif);

     uip_fw_default(&meshif);

    This setup allows the nodes to have 2 network interfaces one for radio communications and one other for serial line communications. Edit

     SLIP setup

    SLIP (Serial Line IP) is build with uIP in every node, but is not activated by default. Only nodes that will receive SLIP packets on their serial line will activate the module, and start using this new interface.

    If you use the tunslip program found in the tools/ directory, you will be able to enable SLIP on the connected node, and send IP packets to this one and the others with the forwarding capability of uIP. The tunslip.sh script lauches tunslip with the default parameters.

    Edit

    Echo server implementation

    The uIP stack is up and running on the gateway node, we have the tunslip network interface ready on the computer, we just have to write the application! The code is in the echo-server.c file.

    The echo server will use a TCP connection. It will listen on port 12345, waiting for connection. Once one is established, it will send a welcome message, then wait for incoming data. Once it has received some it will send it back to the sender and closes the connection.

    Here is the code:

     1: #include "contiki.h"

     2: #include "contiki-net.h"

     3:

     4: #include "dev/leds.h"

     5:

     6: #include

     7: #include

     8:

     9: /*

     10: * We define one protosocket since we've decided to only handle one

     11: * connection at a time. If we want to be able to handle more than one

     12: * connection at a time, each parallell connection needs its own

     13: * protosocket.

     14: */

     15:

     16: static struct psock ps;

     17: /*

     18: * We must have somewhere to put incoming data, and we use a 50 byte

     19: * buffer for this purpose.

     20: */

     21: static char buffer[50];

     22:

     23: /*-----------------------------------------------------*/

Report this document

For any questions or suggestions please email
cust-service@docsford.com