/ iot

[6LoWPAN] UDP Server in Contiki

The mainly motivation of this post is about a friend of mine called Anass who that sent me an e-mail about UDP with Contiki. As in the first post, the tutorial just end with nothing really applicable in engineering, and now I want to teach a simple way to communicate with 6LoWPAN nodes using UDP sockets.

Setup Cooja

As I wrote in the first post, Cooja is a great tool to develop something with Contiki and test before deploy into real nodes, it take much less time to get things up and running. I'll use Cooja because I don't have any 6LoWPAN nodes nearby and it's more easy to show the concept using the simulator. In this setup let's create a simulation like the first post, if you don't have an idea how to do this, what the final video to get something like the image below:
setup_cooja
The mote/node 1 uses the rpl-border-router firmware compiled for the target Z1, as in the tutorial in the link before. For the other motes, I mean, the clients UDP or the "sensors" of our network that'll send data to the gateway we'll create a different firmware.

Client device UDP

Lets create a specific firmware for the motes. Do the following steps:

cd contiki/examples
mkdir udp-demo
cd udp-demo
touch udp-demo.c Makefile Makefile.target project-conf.h

In the Makefile write:

CONTIKI_PROJECT = udp-demo
all: $(CONTIKI_PROJECT)
CONTIKI = ../..
include $(CONTIKI)/Makefile.include

And in the Makefile.target

TARGET = z1

In the project-conf.h, to be quick, let's use some project conf example from erbium CoAP demo:

#ifndef __PROJECT_ERBIUM_CONF_H__
#define __PROJECT_ERBIUM_CONF_H__

#undef NETSTACK_CONF_RDC
#define NETSTACK_CONF_RDC              contikimac_driver

#undef RPL_CONF_MAX_DAG_PER_INSTANCE
#define RPL_CONF_MAX_DAG_PER_INSTANCE     1

#undef UIP_CONF_TCP
#define UIP_CONF_TCP                   0

#if CONTIKI_TARGET_SRF06_CC26XX
#undef NETSTACK_CONF_MAC
#define NETSTACK_CONF_MAC     csma_driver
#else
#undef NETSTACK_CONF_MAC
#define NETSTACK_CONF_MAC     nullmac_driver
#endif

#undef REST_MAX_CHUNK_SIZE
#define REST_MAX_CHUNK_SIZE            48

#undef RPL_CONF_WITH_DAO_ACK
#define RPL_CONF_WITH_DAO_ACK          0

#undef RPL_CONF_OF
#define RPL_CONF_OF                    rpl_of0

#endif 

At the end, use the code below in the udp-demo.c, the code is very simple it just connect to through the simple-udp API to the border router address (tunneled by our SLIP connection) and after sends some data by 2 seconds of interval, but if you have some doubt, just send me an e-mail that I would appreciate to help:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <float.h>
#include "contiki.h"
#include "contiki-net.h"
#include "dev/serial-line.h"
#include "net/ipv6/uip-ds6.h"
#include "sys/etimer.h"
#include "simple-udp.h"
#include "net/ip/uip-debug.h"

#define UDP_PORT_CENTRAL 7878
#define UDP_PORT_OUT 5555
#define CLOCK_REPORT CLOCK_SECOND*2
static struct simple_udp_connection broadcast_connection;
static uip_ipaddr_t server_addr;
static uint16_t central_addr[] = {0xaaaa, 0, 0, 0, 0, 0, 0, 0x1};

void connect_udp_server();
void uip_debug_ipaddr_print(const uip_ipaddr_t *addr);
int ipaddr_sprintf(char *buf, uint8_t buf_len, const uip_ipaddr_t *addr);
int sicslowpan_get_last_rssi();

PROCESS(init_system_proc, "Init system process");
AUTOSTART_PROCESSES(&init_system_proc);

PROCESS_THREAD(init_system_proc, ev, data){
        PROCESS_BEGIN();
        static struct etimer periodic_timer;

        //Init IPv6 network
        uint8_t buff_udp[50],
                device_address[30],
                device_id[17];
        sprintf((char *)device_id,"%02X%02X%02X%02X%02X%02X%02X%02X",
                linkaddr_node_addr.u8[0],linkaddr_node_addr.u8[1],
                linkaddr_node_addr.u8[2],linkaddr_node_addr.u8[3],
                linkaddr_node_addr.u8[4],linkaddr_node_addr.u8[5],
                linkaddr_node_addr.u8[6],linkaddr_node_addr.u8[7]);
        sprintf((char *)device_address,"[%c%c%c%c]-Device-%s",device_id[12],device_id[13],device_id[14],device_id[15],device_id);
        connect_udp_server();
        etimer_set(&periodic_timer, CLOCK_REPORT);
        printf("Device initialized - %s\n", device_address);

        while (1) {
                PROCESS_YIELD();

                int def_rt_rssi = sicslowpan_get_last_rssi();
                char def_rt_str[64];
                memset(def_rt_str, 0, sizeof(def_rt_str));
                ipaddr_sprintf(def_rt_str, sizeof(def_rt_str), uip_ds6_defrt_choose());
                sprintf((char *)buff_udp, "Hello my data -> addr:%02X%02X|%ddBm|%s",linkaddr_node_addr.u8[6],linkaddr_node_addr.u8[7],def_rt_rssi,def_rt_str);

                if (etimer_expired(&periodic_timer)) {
                        etimer_reset(&periodic_timer);
                        printf("Sending data to UDP Server at border router...");
                        simple_udp_sendto(&broadcast_connection, buff_udp, strlen((const char *)buff_udp), &server_addr);
                }
        }
        PROCESS_END();
}

void cb_receive_udp(struct simple_udp_connection *c,
                    const uip_ipaddr_t *sender_addr,
                    uint16_t sender_port,
                    const uip_ipaddr_t *receiver_addr,
                    uint16_t receiver_port,
                    const uint8_t *data,
                    uint16_t datalen) {
        printf("########## UDP #########");
        printf("\nReceived from UDP Server:%s\n",data);
}

void connect_udp_server(){
        uip_ip6addr(&server_addr,
                    central_addr[0],
                    central_addr[1],
                    central_addr[2],
                    central_addr[3],
                    central_addr[4],
                    central_addr[5],
                    central_addr[6],
                    central_addr[7]);
        printf("IPv6 UDP server: ");
        uip_debug_ipaddr_print(&server_addr);

        simple_udp_register(&broadcast_connection,
                            UDP_PORT_OUT,
                            &server_addr,
                            UDP_PORT_CENTRAL,
                            cb_receive_udp);

}

int ipaddr_sprintf(char *buf, uint8_t buf_len, const uip_ipaddr_t *addr)
{
        uint16_t a;
        uint8_t len = 0;
        int i, f;
        for(i = 0, f = 0; i < sizeof(uip_ipaddr_t); i += 2) {
                a = (addr->u8[i] << 8) + addr->u8[i + 1];
                if(a == 0 && f >= 0) {
                        if(f++ == 0) {
                                len += snprintf(&buf[len], buf_len - len, "::");
                        }
                } else {
                        if(f > 0) {
                                f = -1;
                        } else if(i > 0) {
                                len += snprintf(&buf[len], buf_len - len, ":");
                        }
                        len += snprintf(&buf[len], buf_len - len, "%x", a);
                }
        }

        return len;
}

Then, after edited the files, make the project:

make all

Server UDP - Outside of the 6LoWPAN Network

With all copied right, let's create the UDP Server in node.js (you could use any other socket in another language, but this is more fast to use :P). Create the folder and the javascript server as below:

mkdir server-udp
cd server-udp
touch server.js
npm install dgram --save

Paste this code on server.js:

'use strict';
let portIPv6 = 7878;
let hostIPv6 = 'aaaa::1';
let dgram = require('dgram');
let serverUDP = dgram.createSocket('udp6');
let disableLogs = false;

serverUDP.on('listening', function() {
    var address = serverUDP.address();
    console.log('[UDP - IPV6] Active IPv6 server addr.:' + address.address + ":" + address.port);
});

serverUDP.on('message', processMessage);

serverUDP.bind(portIPv6, hostIPv6);

function processMessage(message, remote) {
    if (!disableLogs)
        console.log('[UDP - IPv6] ' + new Date().toISOString() + ' ' + remote.address + ' Port:' + remote.port + ' - ' + message);
    let dataArray = message.toString().split('|');
}

Bring it on!

Go to Cooja and create the UDP Client mote (Motes >> Add motes >> Create new mote type >> Z1 mote...) and point to the firmware compiled before (udp-demo.z1). Once ready, add 4 or 10 motes at time (depends how much RAM you're willing to use :D) in the simulation and start the tunneled bridge with:

cd contiki/tools
sudo ./tunslip6 -a 127.0.0.1 aaaa::1/64  

Start the simulation clicking on START from Cooja panel and then go to the directory of our javascript udp server created before and start it too.

cd contiki/examples/udp-demo/server-udp
node server.js 

If you have done alright, you should have something like this:
final_demo_udp

Yes, each mote/node is reporting yours information in the UDP Server!!!!

The format of the payload message must be:

Hello my data -> addr:Last byte address|RSSI from the last messagedBm|Neighbor address

To help anyone who had some problem in the edit the files, I created a repository with all things configured.

[Link of the repository]
(https://github.com/aignacio/udp-contiki-ipv6)

This example works very well on real devices, in the next tutorial maybe I use some real node to demonstrate the performance behind. If you don't understand some part, just read the first post before comment and see if it's not in that post the answer. If you liked, share and subscribe for more tutorials like this.

References
[6LoWPAN] UDP Server in Contiki
Share this

Subscribe to @aignacio's