wbober gravatar image

Posted 2016-07-06 17:02:57 +0200

blogs->nordicers

Setting up IPv6 over BLE using nRF52 Series and Contiki OS

This tutorial shows you how to set up IPv6 over BLE communication using nRF52832 and Contiki OS.

To fully understand this tutorial you will need to have minimum knowledge of Linux environment and shell. An nRF52 Development Kit and Bluetooth 4.0-compatible hardware are required to run the examples.

Introduction

Contiki OS is an open source operating system for the Internet of Things, see http://www.contiki-os.org/ for more information.

nRF52832 port is Nordic Semiconductor's first Contiki OS compatible platform that supports IPv6 over BLE. It opens up an opportunity to experiment with IPv6 over BLE by leveraging numerous software modules already created for Contiki.

Contiki design and programming model

Before we start experimenting with Contiki OS on nRF52 it’s worthwhile to learn more about it. Contiki OS uses a unique programming model based on protothreads (http://dunkels.com/adam/pt/publications.html). Protothread is a programming abstraction that uses concepts from multi-threaded and event-driven paradigms. It allows for writing code in an easily understood sequential fashion while having a lower memory overhead compared to fully fledged threads. To achieve lower memory consumption, protothreads and processes in Contiki OS do not use individual process stacks to preserve the states, but rather use global variables instead. Due to this, Contiki OS uses cooperative scheduling between context switches. This means that a process cannot be preempted and must explicitly yield control back to the scheduler, so that other processes can be run. A process can yield, for example, by waiting for an event such as timer expiration.

The code snippet below shows an example of a Contiki process which periodically (once a second) blinks an LED. A process is defined using the PROCESS() macro, which creates the process object (“periodic”) and sets a human-readable name to it (“Periodic process”). The process object is automatically started by Contiki since it’s given as an argument to the AUTOSTART_PROCESSES() macro. The process body is defined by using the PROCESS_THREAD() macro and that’s where the process loop is. As seen in the snippet, the process schedules a periodic event timer (etimer) and yields execution by waiting for an event using the PROCESS_WAIT_EVENT() macro. Once the timer triggers, the Contiki scheduler will run the process again from the next line after the PROCESS_WAIT_EVENT() macro.

#include "contiki.h"
#include "dev/leds.h"
#include <stdio.h> /* For printf() */

PROCESS(periodic, "Periodic process");
AUTOSTART_PROCESSES(&periodic);

PROCESS_THREAD(periodic, ev, data)
{
  static struct etimer tick;
  PROCESS_BEGIN();

  etimer_set(&tick, CLOCK_SECOND);

  while (1) {
    PROCESS_WAIT_EVENT();
    if (ev == PROCESS_EVENT_TIMER && etimer_expired(&tick)) {
      leds_toggle(LEDS_1);
      etimer_reset(&tick);
    }
  }

  PROCESS_END();
}

1) Installing the necessary tools

Throughout the rest of the tutorial we’ll assume that we’re using Ubuntu 15.10 Linux distribution. In order to compile for the nRF52 DK platform, you'll need the following tools:

a) nRF5 IOT SDK (https://developer.nordicsemi.com)

Download the nRF5 IOT SDK, extract it to a folder of your choice, and point NRF52_SDK_ROOT environmental variable to it, for example:

wget ttps://developer.nordicsemi.com/nRF5_IoT_SDK/nRF5_IoT_SDK_v0.9.x/nrf5_iot_sdk_3288530.zip
unzip nrf5_iot_sdk_3288530.zip -d $HOME/nrf5x-sdk 
export NRF52_SDK_ROOT=$HOME/nrf5x-sdk

b) Segger JLink Software for Linux (https://www.segger.com/jlink-software.html)

Download and install “Software and documentation pack” using a deb package appropriate for your machine (32bit or 64bit). In order to use the nRF52 DK as a regular Linux user, edit (using sudo) the /etc/udev/rules.d/99-jlink.rules file and replace the ATTR keyword with ATTRS.

c) The remaining tools can be installed using Ubuntu’s packet manager:

 sudo apt-get install gcc-arm-none-eabi make bluez libcap-ng0 radvd picocom git

d) Clone the Contiki OS repository:

cd $HOME
git clone https://github.com/contiki-os/contiki.git

2) Getting started with Hello World

Once all the tools are in place, we can compile and flash a Hello World application. The source code of a very simple Hello World application, which simply prints “Hello, world” on UART, is shown below:

#include "contiki.h"
#include "dev/leds.h"

#include <stdio.h> /* For printf() */

PROCESS(hello_world_process, "Hello world process");
AUTOSTART_PROCESSES(&hello_world_process);

PROCESS_THREAD(hello_world_process, ev, data)
{
  PROCESS_BEGIN();
  printf("Hello, world\n"); 
  PROCESS_END();
}

By compiling and flashing it, we’ll be able to verify that the toolchain setup is correct.

To do this, go to contiki/examples/hello-world and execute in shell:

make TARGET=nrf52dk

If the compilation completes without errors, then it’s time to connect the DK to your PC. If you haven't used the device with Contiki before, erase the device and flash it with a SoftDevice:

make TARGET=nrf52dk erase
make TARGET=nrf52dk softdevice.flash

Finally, the application can be flashed to the device:

make TARGET=nrf52dk hello-world.flash

You can also check if the device prints the “hello world” sentence on the UART by using picocom to view the logs:

sudo picocom -fh -b 38400 --imap lfcrlf /dev/ttyACM0

Since the hello_world_process runs on start and finishes as soon as the line is printed out, you’ll be able to see it every time the DK is reset with the RESET button. The exercise of modifying the Hello World program so that it prints out the sentence every second is left up to the reader!

3) Making an IPv6 connection

We’ve learned how to compile and flash the Contiki application to the DK. Now it’s time to start experimenting with IPv6 connectivity!

The device will start advertising BLE as soon as it's been initialized, which is indicated by the blinking of LED1. To verify that the device is indeed advertising, run the following commands in a shell:

sudo hcitool lescan

The output should be similar to the one shown on the figure below.

image description

Take down the device’s Bluetooth address (in my case that’s 00:87:82:E3:69:17), because you're going to need it later on.

Before we can connect to the device, we need to load and enable the bluetooth_6lowpan kernel module:

# Log in as a root user.
sudo su

# Load 6LoWPAN module.
modprobe bluetooth_6lowpan

# Enable the bluetooth 6lowpan module.
echo 1 > /sys/kernel/debug/bluetooth/6lowpan_enable

Finally, we can make the connection by running the following command (replace 00:AA:BB:CC:DD:EE with your device’s Bluetooth address):

echo "connect 00:AA:BB:CC:DD:EE 1" > /sys/kernel/debug/bluetooth/6lowpan_control

If the command above is completed successfully, LED1 will stop blinking and LED2 will switch on. You can check that by pinging the device using its link-local address. The address has the form of fe80::2aa:bbff:fecc:ddee, where the aa:bb:cc:dd:ee is the device's Bluetooth address. To ping the device, run the following command:

ping6 -I bt0 fe80::2aa:bbff:fecc:ddee

Remember that you’ll have to connect to the device every time you reset it or flash a new firmware. The kernel module will stay loaded until you switch off your PC.

4) Distributing routable IPv6 prefix

Before we can try a more complex example that is described in the next section, we need to make sure that the device has a routable IPv6 address. In Linux, this is done by using the Router Advertisement Daemon (RADVD). To configure RADVD, create a /etc/radvd.conf file (as root) and edit it, so that it has the following content:

interface bt0
{
    AdvSendAdvert on;
    prefix 2001:db8::/64
    {
        AdvOnLink off;
        AdvAutonomous on;
        AdvRouterAddr on;
    };
};

Next, start the RADVD daemon:

# Set IPv6 forwarding (must be present).
sudo echo 1 > /proc/sys/net/ipv6/conf/all/forwarding

# Run radvd daemon.
sudo service radvd restart

If successful, all devices connected to the host will receive a routable 2001:db8 prefix. This can be verified by sending an echo request to the full address:

ping6 -I bt0 2001:db8::2aa:bbff:fecc:ddee

5) Running a CoAP Server on nRF52

With all that preparatory work in place, we can run an IoT service on nRF52! The source code for this example CoAP server for nRF52 is located in `contiki/examples/nrf52dk/coap-demo’.
The CoAP server provides the following resources:

Host
    |-- .well-known
    |   `-- core
    `-- lights
        `-- led3

The ‘.well-known/core’ resource is a mandatory resource that describes the remaining resources available on the device. You can query that resource by using the GET method to retrieve a list of resources in CoRE link format.

The state of LED 3 can be set and queried using the lights/led3 resource. The current state of LED 3 is returned as a text string in payload. Value 0 indicates that LED 3 is off, whereas 1 indicates that LED 3 is on. This resource is also observable. This means that each time the state of lights/led3 resource changes, all interested clients will get a notification with the new state.

The state of the resource can be changed using the POST/PUT method. To switch on the LED, you need to send ‘1’ as payload, whereas to switch it off you need to send ‘0’. The LED can also be toggled using Button 1.

6) Implementing the CoAP server

The server is implemented as a Contiki process. The implementation, shown in the following snippet, is quite simple. We initialize the CoAP service using the rest_init_engine() function and register a single resource using rest_activate_resource(). The latter function binds the resource object res_led3 with a URI "lights/led3".

Next, we activate the Button 1 sensor and enter an endless loop. In this loop, the process waits for an event and toggles the LED whenever the button is pressed. In addition to toggling the LED using leds_toggle(LEDS_3), the process also triggers a CoAP notification on the resource using the res_led3.trigger() function.

PROCESS(er_example_server, "nRF52 DK Coap Server");
AUTOSTART_PROCESSES(&er_example_server);

PROCESS_THREAD(er_example_server, ev, data)
{
  PROCESS_BEGIN();
  PROCESS_PAUSE();

  rest_init_engine();
  rest_activate_resource(&res_led3, "lights/led3");

  SENSORS_ACTIVATE(button_1);

  while (1) {
    PROCESS_WAIT_EVENT();

    if (ev == sensors_event) {
      if (data == &button_1 && button_1.value(BUTTON_SENSOR_VALUE_STATE) == 0) {
        leds_toggle(LEDS_3);
        res_led3.trigger();
      }
    }
  }

  PROCESS_END();
}

The single LED3 resource, which is provided on our server, is implemented in the `resources\res_leds.c’ file. The file contents (reduced a bit for brevity) are shown in the following snippets. A resource object is created using the EVENT_RESOURCE() macro. The macro requires the resource object name, its attributes specified in CoRE link format (here title and observe attributes), as well as functions that handle access to the resource using different CoAP methods. In our case, the resource supports the GET, PUT, and POST methods. Since the DELETE method isn’t allowed, a NULL parameter is given as the sixth argument. The EVENT_RESOURCE() is used to create an observable resource. Hence it also requires defining a method which is called each time the resource state changes. In our case, it is the res_event_handler() method.

EVENT_RESOURCE(res_led3,
               "title=\"LED3\"; obs",
               res_get_handler,
               res_post_put_handler,
               res_post_put_handler,
               NULL,
               res_event_handler
               );

The POST/PUT method handler is implemented by the res_post_put_handler() function. We get a pointer to the payload buffer using REST.get_request_payload() and then check if the first byte is either ‘0’ o ‘1’. If so, we set the LED state accordingly, notify observing clients, and indicate that the resource was changed using the REST.set_response_status() function.

static void
res_post_put_handler(void *request, void *response, uint8_t *buffer,
                     uint16_t preferred_size, int32_t *offset)
{
  const uint8_t *payload;
  REST.get_request_payload(request, &payload);

  if (*payload == '0' || *payload == '1') {
    if (*payload == '1') {
      leds_on(LEDS_3);
    } else {
      leds_off(LEDS_3);
    }
    REST.notify_subscribers(&res_led3);
    REST.set_response_status(response, REST.status.CHANGED);
  } else {
    REST.set_response_status(response, REST.status.BAD_REQUEST);
  }
}

The GET method handler, shown in the snippet above, simply formats the LED state as a single character and places it in the message payload. We also set the response content type to text, so that the client has a better understanding of how to interpret the data inside the CoAP message.

static void
res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset)
{
  REST.set_header_content_type(response, REST.type.TEXT_PLAIN);
  REST.set_response_payload(response, buffer, snprintf((char *)buffer,     preferred_size, "%d", (leds_get() & LEDS_3) ? 1 : 0));
}

The event handler doesn’t have much to do apart from sending a notification to clients which are observing the resource.

static void
res_event_handler()
{
  REST.notify_subscribers(&res_led3);
}

In order to compile and flash the CoAP server to the DK, execute:

make TARGET=nrf52dk coap-server.flash

Next, make an IPv6 connection to the DK using the procedure described in the previous paragraphs.

7) Using Mozilla Firefox as a CoAP client

Once the connection has been established, which is indicated by LED2 being on, we can finally play with it. For this you can use Mozilla Firefox with Copper plug-in. You can simply install the plug-in using Firefox’s extensions menu. The plug-in kicks in automatically whenever you enter a URI that uses the coap:// scheme. After this, a screen similar to the one show in the Figure below will appear.

image description

To discover resources provided by the server, click on the device IPv6 address in the Resources tree and then click on the Discover icon. This will read the contents of the .well-known/core resource.

To query the current LED state, simply select it from the Resources tree, and click on the GET icon. To switch the LED on or off, enter 1 or 2 respectively in the Payload field and click on the PUT icon. You can also observe the resource state by selecting it and clicking on the Observe icon.

Conclusion

This post demonstrated how to use the nRF52 port for Contiki OS. We’ve discussed how to write and run simple applications using Contiki OS and the nRF52 DK. We’ve also experimented with running IPv6 over BLE by implementing a simple CoAP server.

12 comments

msmayling gravatar image

Posted Oct. 3, 2016, 4:04 a.m.

Hi,

I've tried your examples, and get everything running and connected, but in neither case can I get a response to a ping. I routinely use the IPV6 setup to talk to regular Nordic examples, which work fine, so I know my linux box and connection method work for native nRF52 applications. (TFTP, SNTP, COAP-SERVER).

Is there something else in the setup that needs to be done? Obviously, if the ping isn't working, other communications are not either. Or, is IOT SDK 1.0 ready to test in this environment? I would really like to have the Contiki capabilities.

This is great progress with the nRF52 support and application capability, so thank you for the nice job so far.

Regards, Mike

wbober gravatar image

Posted Oct. 3, 2016, 4:26 p.m.

Hi Mike S,

It should work out of the box. I'll go through the example and let you know. In the meantime can you please provide more information about your setup (kernel version and os, iot sdk version, dk version, etc)? Basically anything that might help to trace the issue?

Regards, Wojtek

msmayling gravatar image

Posted Oct. 5, 2016, 12:38 a.m.

Hi, Wojtek,

I'm running Ubuntu 16.04, linux kernel 4.7.2. (I get the same results with 4.6.2). I'm using Contiki loaded as described above, and the nrf5x-sdk described above, including the IOT SDK 0.9. (Is 1.0 coming soon?)

Both the hello-world and coap-server examples compile fine. I do get a segmentation fault when I try the make TARGET=nrf52dk erase commant; I just use nRF studio on a windows pc to erase it. Writing the softdevice works fine. Writing the programs seems to work fine. Both programs show messages on the terminal indicating they started and are running, and the advertising light is blinking.

I can see the pca10040 board with lescan, and can connect. When running the Contiki examples, I get a "serious problem occurred" warning from Ubuntu right after connecting. The board shows the "connected" light, but does not respond to pings.

With exactly the same configuration, I go to the nrf5x-sdk directory, and go to the examples. (I have used these many times on the windows side with no problems.) I compile the coap-server, and flash it with nrfjprog. The board advertises, I can connect (the LED2 comes on), and I can run a COAP-client with FireFox-Cu and it runs fine.

Is there a Contiki Coap-server with more diagnostics? For example, send a message to the terminal with the connection status, or a message when a ping arrives. I have also used the commanc iftop -i bt0 to look at the packets going back and forth. The standard example show the pca10040 address responding, the Contiki examples show nothing coming back.

I hope this helps narrow down the possible problem.

Thank you, Mike

wbober gravatar image

Posted Oct. 6, 2016, 2:10 p.m.

Hi Mike,

Thanks for the details. It's a side issue but could you please check the version of nrfjprog (nrfjprog --version)? I'm using (nrfjprog version: 9.0.0, JLinkARM.dll version: 5.10f) and I haven't experienced any segfaults, so it would be worthwile to see what's going on.

I have a bit of trouble with my BLE HW setup, so I'll need a day or so before I can test the issue and get back to you. Sorry about that.

It's interesting that the IOT SDK examples work but Contiki doesn't. Perhaps there were some changes introduced in the mainline Contiki which broke the port. You could verify this by using nrf52dk-pr branch from https://github.com/wbober/contiki/tree/nrf52dk-pr. That's the version of Contiki which was used for writing this blog post.

To get debug logs, you'll need to set DEBUG macro to 1 in each module you'd like to debug. Typically the macro is at the top of the source file.

Note that debug logs might produce quite a lot of output, hence I'd recommend you use RTT logging rather than UART. There is a section on how to enable and use in the platfrom/nrf52dk/README.md.

Hope this helps.

wbober gravatar image

Posted Oct. 7, 2016, 10:26 a.m.

Hi Mike,

I've tested the issue on Ubuntu 15.10 and 16.04 and I can confirm that support for nRF52 in Contiki master branch is indeed broken. For the time being please use nrf52dk-pr branch, from my repo. I've tested examples/hello-world and I get a ping reply.

I'll look into the issue but I need to first figure out with my team if the task goes to the current or the next sprint.

Thanks for letting me know about the issue.

Regards, Wojtek

msmayling gravatar image

Posted Oct. 8, 2016, 5:21 p.m.

Hi, Wojtek,

Thank you for the update. I'm glad you could confirm the issue. I'll look forward to the revised master branch. Just FYI, I am using nrfjprog 9.0.0 as you are, and JLINKARM.dll V5.12i. These work with your standard examples, so I hope they will be compatible with any Contiki ports to nRF52.

Regards,

Mike

dimonomid gravatar image

Posted Oct. 18, 2016, 9:01 p.m.

Just in case someone experiences troubles with routable ipv6 prefix: for the routable ipv6 prefix to work, I had to execute the following before restarting radvd:

sudo ifconfig bt0 add 2001:db8::/64
Alijaan gravatar image

Posted Nov. 22, 2016, 3:03 p.m.

hi, i am using the VisualGDB on windows 10 OS. i simple download the nrf5_io_sdk and contiki source code from github. i try to Run the Hello world. but i got problem in the start. he give me the error in the "pfs.h" please check the image.
image description

thank you.

wbober gravatar image

Posted Nov. 23, 2016, 10:54 a.m.

Hi Alijaan,

It looks like a problem with your environment and Contiki rather than with the port itself. I don't have any experience with VisualGDB, sorry. Perhaps you could try to ask about the issue on Contiki mailing list?

Kind regards, Wojtek

raju_ga153 gravatar image

Posted Jan. 16, 2017, 6:05 p.m.

Hi Wbober,

As you said the Master branch on Contiki project has been broken for nrf52DK. Is there any plan to update Master branch to support for nrf52DK? If so, when we can expect the same.

Thanks, Raju

wbober gravatar image

Posted Jan. 17, 2017, 10:28 a.m.

Hi Raju,

The issue seems to be caused by some changes introduced to Contiki a few months ago by Contiki developers.

I've submitted the issue (see https://github.com/contiki-os/contiki/issues/2014), but I didn't get any reply.

youssif.saeed gravatar image

Posted March 24, 2017, 4:25 p.m.

Hi

One thing I didn't find mentioned anywhere is how to get the URI address used in step 7 (Using Mozilla Firefox as a CoAP client). Is this a hardcoded address or is it something we have to deduce from the remote address?

Thanks

Sign in to comment.

User menu

    or sign up

Recent questions

Related posts by tag