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 <stdio.h> /* 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.

窗体顶端

窗体底端

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 <stdio.h> /* 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.

窗体顶端

窗体底端

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… 

窗体顶端

窗体底端

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. 

窗体顶端

窗体底端

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. 

窗体顶端

窗体底端

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 <stdio.h>

  7: #include <string.h>

  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: /*-----------------------------------------------------*/

 24: /*

 25:  * A protosocket always requires a protothread. The protothread

 26:  * contains the code that uses the protosocket. We define the

 27:  * protothread here.

 28:  */

 29: static PT_THREAD(handle_connection(struct psock *p))

 30: {

 31:   /*

 32:    * A protosocket's protothread must start with a PSOCK_BEGIN(), with

 33:    * the protosocket as argument.

 34:    *

 35:    * Remember that the same rules as for protothreads apply: do NOT

 36:    * use local variables unless you are very sure what you are doing!

 37:    * Local (stack) variables are not preserved when the protothread

 38:    * blocks.

 39:    */

 40:   PSOCK_BEGIN(p);

 41:

 42:   /*

 43:    * We start by sending out a welcoming message. The message is sent

 44:    * using the PSOCK_SEND_STR() function that sends a null-terminated

 45:    * string.

 46:    */

 47:   PSOCK_SEND_STR(p, "Welcome, please type something and press return.\n");

 48:

 49:   /*

 50:    * Next, we use the PSOCK_READTO() function to read incoming data

 51:    * from the TCP connection until we get a newline character. The

 52:    * number of bytes that we actually keep is dependant of the length

 53:    * of the input buffer that we use. Since we only have a 10 byte

 54:    * buffer here (the buffer[] array), we can only remember the first

 55:    * 10 bytes received. The rest of the line up to the newline simply

 56:    * is discarded.

 57:    */

 58:   PSOCK_READTO(p, '\n');

 59:

 60:   /*

 61:    * And we send back the contents of the buffer. The PSOCK_DATALEN()

 62:    * function provides us with the length of the data that we've

 63:    * received. Note that this length will not be longer than the input

 64:    * buffer we're using.

 65:    */

 66:   PSOCK_SEND_STR(p, "Got the following data: ");

 67:   PSOCK_SEND(p, buffer, PSOCK_DATALEN(p));

 68:   PSOCK_SEND_STR(p, "Good bye!\r\n");

 69:

 70:   /*

 71:    * We close the protosocket.

 72:    */

 73:   PSOCK_CLOSE(p);

 74:

 75:   /*

 76:    * And end the protosocket's protothread.

 77:    */

 78:   PSOCK_END(p);

 79: }

 80: /*----------------------------------------------------------------*/

 81: /*

 82:  * We declare the process.

 83:  */

 84: PROCESS(example_psock_server_process, "Example protosocket server");

 85: AUTOSTART_PROCESSES(&example_psock_server_process);

 86: /*----------------------------------------------------------------*/

 87: /*

 88:  * The definition of the process.

 89:  */

 90: PROCESS_THREAD(example_psock_server_process, ev, data)

 91: {

 92:   /*

 93:    * The process begins here.

 94:    */

 95:   PROCESS_BEGIN();

 96:

 97:   /*

 98:    * We start with setting up a listening TCP port. Note how we're

 99:    * using the HTONS() macro to convert the port number (1234) to

100:    * network byte order as required by the tcp_listen() function.

101:    */

102:   tcp_listen(HTONS(12345));

103:

104:   /*

105:    * We loop for ever, accepting new connections.

106:    */

107:   while(1) {

108:

109:     /*

110:      * We wait until we get the first TCP/IP event, which probably

111:      * comes because someone connected to us.

112:      */

113:     PROCESS_WAIT_EVENT_UNTIL(ev == tcpip_event);

114:

115:     /*

116:      * If a peer connected with us, we'll initialize the protosocket

117:      * with PSOCK_INIT().

118:      */

119:     if(uip_connected()) {

120:

121:       /*

122:        * The PSOCK_INIT() function initializes the protosocket and

123:        * binds the input buffer to the protosocket.

124:        */

125:       PSOCK_INIT(&ps, buffer, sizeof(buffer));

126:

127:       /*

128:        * We loop until the connection is aborted, closed, or times out.

129:        */

130:       while(!(uip_aborted() || uip_closed() || uip_timedout())) {

131:

132:         /*

133:          * We wait until we get a TCP/IP event. Remember that we

134:          * always need to wait for events inside a process, to let

135:          * other processes run while we are waiting.

136:          */

137:         PROCESS_WAIT_EVENT_UNTIL(ev == tcpip_event);

138:

139:         /*

140:          * Here is where the real work is taking place: we call the

141:          * handle_connection() protothread that we defined above. This

142:          * protothread uses the protosocket to receive the data that

143:          * we want it to.

144:          */

145:         handle_connection(&ps);

146:       }

147:     }

148:   }

149:

150:   /*

151:    * We must always declare the end of a process.

152:    */

153:   PROCESS_END();

154: }

155: /*-------------------------------------------------------------*/

echo-server.c

You can now compile the project, and program a device with it. 

For SLIP, assuming you're using the /dev/ttyS0 serial port of your computer, type make then run ./tunslip.sh as root in the tools directory. You should observe the following: 

[root@brigand tools]# ./tunslip.sh

./tunslip -s /dev/ttyS0 -B 115200 172.16.0.0 255.255.0.0

slip started on ``/dev/ttyS0''

opened device ``/dev/tun0''

ifconfig tun0 inet `hostname` up

route add -net 172.16.0.0 netmask 255.255.0.0 dev tun0

ifconfig tun0

tun0      Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00

          inet addr:194.199.21.252  P-t-P:194.199.21.252  Mask:255.255.255.255

          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1500  Metric:1

          RX packets:0 errors:0 dropped:0 overruns:0 frame:0

          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0

          collisions:0 txqueuelen:500

          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)

This is waiting for a connection from the WSN430. Power on the WSN430 you've just programmed, already plugged on the serial link, you should see: 

Contiki 2.3 started. Rime address is 1.109

MAC ff:00:00:12:91:bf:6d:01

 X-MAC channel 26

uIP started with IP address 172.16.1.109

Starting 'Example protosocket server'

1.109: making myself the IP network gateway.

IPv4 address of the gateway: 172.16.1.109

route add -net 172.16.1.109 netmask 255.255.255.255 dev tun0

You have the IP of the node (172.16.1.109 here) printed. You can now either ping the device (ping 172.16.1.109) or connect to the server we've created using netcat (nc 172.16.1.109 12345) 

窗体顶端

窗体底端

Echo Server (UDP)

The example found in the examples/04_echo-udp folder implements a simple UDP server application. The source code is found in the echo-serve.c file. 

UDP is connectionless, therefore programming a UDP application is rather simple. Only two things can be done with UDP: sending and receiving datagrams. Contiki uses the word 'connection' both for UDP and TCP, but in the sense of a BSD socket. It is an endpoint for Internet communication. Here are the different functions/macros used for programming using UDP: 

· struct uip_udp_conn * udp_new (const uip_ipaddr_t *ripaddr, u16_t port, void *appstate): this function creates a new UDP 'connection'. If the connection is used to send data to a specific host, the arguments ripaddr (remote IP address) and port must be filled adequately, otherwise ripaddr may be set to NULL and port to 0 in order to accept any incoming datagram. appstate is a pointer to some data that will be passed to the process when UDP events will occur. The function returns a pointer to the connection, that will be used by all the other functions afterward. The function chooses a local port number arbitrarily, which can be overwritten as explained below;

· udp_bind(conn, port): this macro binds an open UDP connection to a local port number. The specified port will be the source port used to send datagrams as well as the listening port for incoming datagrams;

· uip_newdata(): this macro indicates if there is data available on the connection. If so it can be accessed from the pointer uip_appdata and the length obtained by uip_datalen().

The Echo Server UDP uses most of these functions. It listens on UDP port 50000 and replies back incoming datagrams. The complete source code of the application is here: 

 1: /**

 2:  * \file

 3:  *         A network application that listens on UDP port 50000 and echoes.

 4:  * \author

 5:  *         Clément Burin des Roziers <clement.burin-des-roziers@inrialpes.fr>

 6:  */

 7:

 8: #include "contiki.h"

 9: #include "contiki-net.h"

10:

11: #include "dev/leds.h"

12:

13: #include <stdio.h>

14: #include <string.h>

15:

16: #define PRINTF printf

17:

18: #define UDP_DATA_LEN 120

19: #define UDP_HDR ((struct uip_udpip_hdr *)&uip_buf[UIP_LLH_LEN])

20:

21:

22: static struct uip_udp_conn *udpconn;

23: static uint8_t udpdata[UDP_DATA_LEN] = "rx=";

24:

25: static void udphandler(process_event_t ev, process_data_t data)

26: {

27:     if (uip_newdata())

28:     {

29:         /* Set the last byte of the received data as 0 in order to print it. */

30:         int len = uip_datalen();

31:         ((char *)uip_appdata)[len] = 0;

32:         printf("Received from %u.%u.%u.%u:%u: '%s'\n", uip_ipaddr_to_quad(&UDP_HDR->srcipaddr), HTONS(UDP_HDR->srcport), (char*)uip_appdata);

33:

34:         /* Prepare the response datagram in a local buffer */

35:         memcpy(udpdata, "rx=", 3);

36:         memcpy(udpdata+3, uip_appdata, len);

37:

38:         /* Copy the information about the sender to the udpconn in order to reply */

39:         uip_ipaddr_copy(&udpconn->ripaddr , &UDP_HDR->srcipaddr); // ip address

40:         udpconn->rport = UDP_HDR->srcport; // UDP port

41:

42:         /* Send the reply datagram */

43:         printf("sending back\n");

44:         uip_udp_packet_send(udpconn, udpdata, uip_datalen()+3);

45:

46:         /* Restore the udpconn to previous setting in order to receive other packets */

47:         uip_ipaddr_copy(&udpconn->ripaddr , &uip_all_zeroes_addr);

48:         udpconn->rport = 0;

49:     }

50: }

51:

52: /*----------------------------------------------------------------*/

53: /*

54:  * We declare the process.

55:  */

56: PROCESS(example_udp_server_process, "Example UDP server");

57: AUTOSTART_PROCESSES(&example_udp_server_process);

58: /*----------------------------------------------------------------*/

59: /*

60:  * The definition of the process.

61:  */

62: PROCESS_THREAD(example_udp_server_process, ev, data)

63: {

64:

65:     uip_ipaddr_t ipaddr;

66:

67:     PROCESS_BEGIN();

68:     printf("UDP Echo Server test\n");

69:

70:     /* Create a UDP 'connection' with IP 0.0.0.0 and port 0 as remote host.

71:      * This means the stack will accepts UDP datagrams from any node. */

72:     udpconn = udp_new(NULL, HTONS(0), NULL);

73:

74:     /* Bind the UDP 'connection' to the port 50000. That's the port we're listening on. */

75:     udp_bind(udpconn, HTONS(50000));

76:

77:     printf("listening on UDP port %u\n", HTONS(udpconn->lport));

78:

79:     while(1) {

80:         /* Wait until we have an event caused by tcpip interaction */

81:         PROCESS_WAIT_EVENT_UNTIL(ev == tcpip_event);

82:         /* Handle it */

83:         udphandler(ev, data);

84:     }

85:

86:     PROCESS_END();

87: }

88: /*------------------------------------------------*/

echo-server.c

The application starts in the process definition. First a UDP connection is created, specifying remote IP address and port as 0, indicating it's a listening connection. Then the local UDP port is bound to 50000, which means the connection listens on that port. Finally, an infinite loop is entered waiting for TCPIP events, and calling a handler function when these occur. 

The handler function checks if new data is available from the uIP buffer. If so, it prints the received data for debug as well as the sender IP address and port, then prepare a response datagram by prepending “rx=” to the received message. The connection information must be updated in order to send the response to the emitter of the message. Therefore the ripaddr and rport fields of the UDP connection structure are filled with the sender information extracted from the received IP packet. Then the function for sending the packet is invoked. Finally the UDP connection structure is updated back to its original state (with remote IP address and port set to 0) in order to be able to receive datagrams from any host. 

Run tunslip as in the previous example and execute nc -u 176.12.xxx.xxx 50000 in a terminal to test the echo server. 

窗体顶端

窗体底端

IPv6 UDP Communication

The examples found in the 05_ipv6 folder implement a UDP-server and a UDP-client applications, using UDP and IPv6. The first example will run on a node, listening on a given UDP port and waiting fir datagrams to arrive. The second example will send datagrams to the server periodically. 

To enable IPv6, the following line must be added to the project's Makefile: 

UIP_CONF_IPV6=1

Here is the code for the server example: 

  1: /*

  2:  * Redistribution and use in source and binary forms, with or without

  3:  * modification, are permitted provided that the following conditions

  4:  * are met:

  5:  * 1. Redistributions of source code must retain the above copyright

  6:  *    notice, this list of conditions and the following disclaimer.

  7:  * 2. Redistributions in binary form must reproduce the above copyright

  8:  *    notice, this list of conditions and the following disclaimer in the

  9:  *    documentation and/or other materials provided with the distribution.

 10:  * 3. Neither the name of the Institute nor the names of its contributors

 11:  *    may be used to endorse or promote products derived from this software

 12:  *    without specific prior written permission.

 13:  *

 14:  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND

 15:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE

 16:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE

 17:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE

 18:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL

 19:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS

 20:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)

 21:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT

 22:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY

 23:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF

 24:  * SUCH DAMAGE.

 25:  *

 26:  * This file is part of the Contiki operating system.

 27:  *

 28:  */

 29:

 30: #include "contiki.h"

 31: #include "contiki-lib.h"

 32: #include "contiki-net.h"

 33:

 34: #include <string.h>

 35:

 36: #define DEBUG 1

 37: #if DEBUG

 38: #include <stdio.h>

 39: #define PRINTF(...) printf(__VA_ARGS__)

 40: #define PRINT6ADDR(addr) PRINTF(" %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x ", ((u8_t *)addr)[0], ((u8_t *)addr)[1], ((u8_t *)addr)[2], ((u8_t *)addr)[3], ((u8_t *)addr)[4], ((u8_t *)addr)[5], ((u8_t *)addr)[6], ((u8_t *)addr)[7], ((u8_t *)addr)[8], ((u8_t *)addr)[9], ((u8_t *)addr)[10], ((u8_t *)addr)[11], ((u8_t *)addr)[12], ((u8_t *)addr)[13], ((u8_t *)addr)[14], ((u8_t *)addr)[15])

 41: #define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5])

 42: #else

 43: #define PRINTF(...)

 44: #define PRINT6ADDR(addr)

 45: #define PRINTLLADDR(addr)

 46: #endif

 47:

 48: #define UDP_IP_BUF   ((struct uip_udpip_hdr *)&uip_buf[UIP_LLH_LEN])

 49:

 50: #define MAX_PAYLOAD_LEN 120

 51:

 52: static struct uip_udp_conn *server_conn;

 53:

 54: PROCESS(udp_server_process, "UDP server process");

 55: AUTOSTART_PROCESSES(&udp_server_process);

 56: /*----------------------------------------------------------------*/

 57: static void

 58: tcpip_handler(void)

 59: {

 60:   static int seq_id;

 61:   char buf[MAX_PAYLOAD_LEN];

 62:

 63:   if(uip_newdata()) {

 64:     ((char *)uip_appdata)[uip_datalen()] = 0;

 65:     PRINTF("Server received: '%s' from ", (char *)uip_appdata);

 66:     PRINT6ADDR(&UDP_IP_BUF->srcipaddr);

 67:     PRINTF("\n");

 68:

 69:     uip_ipaddr_copy(&server_conn->ripaddr, &UDP_IP_BUF->srcipaddr);

 70:     server_conn->rport = UDP_IP_BUF->srcport;

 71:     PRINTF("Responding with message: ");

 72:     sprintf(buf, "Hello from the server! (%d)", ++seq_id);

 73:     PRINTF("%s\n", buf);

 74:

 75:     uip_udp_packet_send(server_conn, buf, strlen(buf));

 76:     /* Restore server connection to allow data from any node */

 77:     memset(&server_conn->ripaddr, 0, sizeof(server_conn->ripaddr));

 78:     server_conn->rport = 0;

 79:   }

 80: }

 81: /*---------------------------------------------------------------*/

 82: static void

 83: print_local_addresses(void)

 84: {

 85:   int i;

 86:   uip_netif_state state;

 87:

 88:   PRINTF("Server IPv6 addresses: \n");

 89:   for(i = 0; i < UIP_CONF_NETIF_MAX_ADDRESSES; i++) {

 90:     state = uip_netif_physical_if.addresses[i].state;

 91:     if(state == TENTATIVE || state == PREFERRED) {

 92:       PRINT6ADDR(&uip_netif_physical_if.addresses[i].ipaddr);

 93:       PRINTF("\n");

 94:     }

 95:   }

 96: }

 97: /*-----------------------------------------------------------*/

 98: PROCESS_THREAD(udp_server_process, ev, data)

 99: {

100:   static struct etimer timer;

101:

102:   PROCESS_BEGIN();

103:   PRINTF("UDP server started\n");

104:

105:   // wait 3 second, in order to have the IP addresses well configured

106:   etimer_set(&timer, CLOCK_CONF_SECOND*3);

107:

108:   // wait until the timer has expired

109:   PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER);

110:

111:   print_local_addresses();

112:

113:   // set NULL and 0 as IP address and port to accept packet from any node and any srcport.

114:   server_conn = udp_new(NULL, HTONS(0), NULL);

115:   udp_bind(server_conn, HTONS(3000));

116:

117:   PRINTF("Server listening on UDP port %u\n", HTONS(server_conn->lport));

118:

119:   while(1) {

120:     PROCESS_YIELD();

121:     if(ev == tcpip_event) {

122:       tcpip_handler();

123:     }

124:   }

125:

126:   PROCESS_END();

127: }

128: /*----------------------------------------------------------------*/

udp-server.c

And here's the code for the client example: 

  1: /*

  2:  * Redistribution and use in source and binary forms, with or without

  3:  * modification, are permitted provided that the following conditions

  4:  * are met:

  5:  * 1. Redistributions of source code must retain the above copyright

  6:  *    notice, this list of conditions and the following disclaimer.

  7:  * 2. Redistributions in binary form must reproduce the above copyright

  8:  *    notice, this list of conditions and the following disclaimer in the

  9:  *    documentation and/or other materials provided with the distribution.

 10:  * 3. Neither the name of the Institute nor the names of its contributors

 11:  *    may be used to endorse or promote products derived from this software

 12:  *    without specific prior written permission.

 13:  *

 14:  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND

 15:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE

 16:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE

 17:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE

 18:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL

 19:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS

 20:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)

 21:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT

 22:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY

 23:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF

 24:  * SUCH DAMAGE.

 25:  *

 26:  * This file is part of the Contiki operating system.

 27:  *

 28:  */

 29:

 30: #include "contiki.h"

 31: #include "contiki-lib.h"

 32: #include "contiki-net.h"

 33:

 34: #include <string.h>

 35:

 36: #define DEBUG 1

 37: #if DEBUG

 38: #include <stdio.h>

 39: #define PRINTF(...) printf(__VA_ARGS__)

 40: #define PRINT6ADDR(addr) PRINTF(" %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x ", ((u8_t *)addr)[0], ((u8_t *)addr)[1], ((u8_t *)addr)[2], ((u8_t *)addr)[3], ((u8_t *)addr)[4], ((u8_t *)addr)[5], ((u8_t *)addr)[6], ((u8_t *)addr)[7], ((u8_t *)addr)[8], ((u8_t *)addr)[9], ((u8_t *)addr)[10], ((u8_t *)addr)[11], ((u8_t *)addr)[12], ((u8_t *)addr)[13], ((u8_t *)addr)[14], ((u8_t *)addr)[15])

 41: #define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5])

 42: #else

 43: #define PRINTF(...)

 44: #define PRINT6ADDR(addr)

 45: #define PRINTLLADDR(addr)

 46: #endif

 47:

 48: #define SEND_INTERVAL        15 * CLOCK_SECOND

 49: #define MAX_PAYLOAD_LEN        40

 50:

 51: static struct uip_udp_conn *client_conn;

 52: /*---------------------------------------------------------------------------*/

 53: PROCESS(udp_client_process, "UDP client process");

 54: AUTOSTART_PROCESSES(&udp_client_process);

 55: /*---------------------------------------------------------------------------*/

 56: static void

 57: tcpip_handler(void)

 58: {

 59:   char *str;

 60:

 61:   if(uip_newdata()) {

 62:     str = uip_appdata;

 63:     str[uip_datalen()] = '\0';

 64:     printf("Response from the server: '%s'\n", str);

 65:   }

 66: }

 67: /*---------------------------------------------------------------------------*/

 68: static void

 69: timeout_handler(void)

 70: {

 71:   static int seq_id;

 72:   char buf[MAX_PAYLOAD_LEN];

 73:

 74:   printf("Client sending to: ");

 75:   PRINT6ADDR(&client_conn->ripaddr);

 76:   sprintf(buf, "Hello %d from the client", ++seq_id);

 77:   printf(" (msg: %s)\n", buf);

 78:   uip_udp_packet_send(client_conn, buf, strlen(buf));

 79: }

 80: /*---------------------------------------------------------------------------*/

 81: static void

 82: print_local_addresses(void)

 83: {

 84:   int i;

 85:   uip_netif_state state;

 86:

 87:   PRINTF("Client IPv6 addresses: ");

 88:   for(i = 0; i < UIP_CONF_NETIF_MAX_ADDRESSES; i++) {

 89:     state = uip_netif_physical_if.addresses[i].state;

 90:     if(state == TENTATIVE || state == PREFERRED) {

 91:       PRINT6ADDR(&uip_netif_physical_if.addresses[i].ipaddr);

 92:       PRINTF("\n");

 93:     }

 94:   }

 95: }

 96: /*---------------------------------------------------------------------------*/

 97: static void

 98: set_connection_address(uip_ipaddr_t *ipaddr)

 99: {

100:   // change this IP address depending on the node that runs the server!

101:   uip_ip6addr(ipaddr,0xfe80,0,0,0,0x8400,0x0012,0x91c7,0x1b01);

102: }

103: /*---------------------------------------------------------------------------*/

104: PROCESS_THREAD(udp_client_process, ev, data)

105: {

106:   static struct etimer et;

107:   uip_ipaddr_t ipaddr;

108:

109:   PROCESS_BEGIN();

110:   PRINTF("UDP client process started\n");

111:

112:   // wait 3 second, in order to have the IP addresses well configured

113:   etimer_set(&et, CLOCK_CONF_SECOND*3);

114:

115:   // wait until the timer has expired

116:   PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER);

117:

118:   print_local_addresses();

119:   set_connection_address(&ipaddr);

120:

121:   /* new connection with remote host */

122:   client_conn = udp_new(&ipaddr, HTONS(3000), NULL);

123:

124:   PRINTF("Created a connection with the server ");

125:   PRINT6ADDR(&client_conn->ripaddr);

126:   PRINTF("local/remote port %u/%u\n",

127:     HTONS(client_conn->lport), HTONS(client_conn->rport));

128:

129:   etimer_set(&et, SEND_INTERVAL);

130:   while(1) {

131:     PROCESS_YIELD();

132:     if(etimer_expired(&et)) {

133:       timeout_handler();

134:       etimer_restart(&et);

135:     } else if(ev == tcpip_event) {

136:       tcpip_handler();

137:     }

138:   }

139:

140:   PROCESS_END();

141: }

142: /*---------------------------------------------------------------------------*/

udp-client.c

The IPv6 local-link addresses of the nodes are determined automatically using Stateless Address Autoconfiguration. Each node appends its link-layer 6byte address to the local-link network prefix (fe80::0/64). 

When the tcpip_process is started, the address configuration is done, then some IPv6 ND messages are exchanged (such as Duplicate Address Detection and Router Sollicitation). If a router is present, it may give some extra addresses to the node.The stack is now ready to operate at least on a local link scope. 

The procedure to send and receive UDP datagrams is now similar to when IPv4 was used. The UDP connection must be filled with the other node's IP address and the destination port used, and the local port number may be specified. Then the functions called and events are the same. 

To try the communication between 2 nodes, make sure that you update the udp-client.c with the server IP address. 

If you want to interact with your node UDP server from a PC, you can use the tools/uip6-bridge application on a node linked to the PC with a serial link. The PC should run tapslip6 and be configure to act as an IPv6 router. The procedure is described in the tools/uip6-bridge/README file. 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值