CoAP -03

From TinyOS Wiki
Revision as of 08:29, 8 June 2011 by Thp (talk | contribs) (Compile and install CoapBlip application)
Jump to: navigation, search

TinyOS CoAP

This page describes how to setup the TinyOS CoAP implementation based on libcoap.

This adaptation uses the TinyOS blip stack for UDP communication.

libcoap implements the Constrained Application Protocol (CoAP) [[1]] based on the following drafts:

  • draft-ietf-core-coap-03
  • draft-ietf-core-link-format-01
  • draft-ietf-core-block-00
  • draft-ietf-core-observe-00
  • draft-bormann-coap-misc-06

libcoap provides sample CoAP server and client implementations, which have participated in several plug-fests of the IETF CoRE working group.

TinyOS libcoap Adaptation Limitations

  • only GET and PUT methods supported, POST and DELETE methods not supported
  • draft-ietf-core-block-00, draft-ietf-core-observe-00 not yet implemented, code needs to be changed from time.h to TinyOS timers
  • currently only content-type application/octet-stream implemented
  • .well-known/core resource is not created

The code has been tested on TelosB nodes only, yet. The sample implementation depends on TelosB sensors.

Installation instructions

Get the code

The CoAP code is part of the tinyos-main SVN repository from revision r5623.

Get it from the SVN according to http://code.google.com/p/tinyos-main/source/checkout .

Then set the TOSROOT, TOSDIR and MAKERULES environment variables appropriately.

export TOSROOT=your tinyos-main directory (e.g. /home/USER/src/tinyos-main)
export TOSDIR=$TOSROOT/tos
export MAKERULES=$TOSROOT/support/make/Makerules

Compile libcoap and examples

cd $TOSROOT/support/sdk/c/coap
autoconf
./configure
make

Compile and install CoapBlip application

To install the CoAP application on the mote, attach the mote via USB and run the following set of commands:

cd $TOSROOT/apps/CoapBlip
make telosb blip coap install,3 bsl,/dev/ttyUSB0

Setting up PppRouter and ppp connection

To install the PppRouter application for IPv6 support on the second attached mote, execute

cd $TOSROOT/apps/PppRouter

and edit the Makefile by commenting the following line (20) out:

PFLAGS += -DBLIP_DERIVE_SHORTADDRS

If you are using TelosB motes (or any other mote with low memory capacity), open PppRouterC.nc and comment line 45 out:

components UDPShellC;

Then install the PppRouter on the mote:

make telosb blip install bsl,/dev/ttyUSB1

Next, start the ppp connection by executing

sudo pppd debug passive noauth nodetach 115200 /dev/ttyUSB1 nocrtscts nocdtrcts lcp-echo-interval 0 noccp noip ipv6 ::23,::24

After entering the sudo password, a similar output should be printed on your screen:

using channel 1
Using interface ppp0
Connect: ppp0 <--> /dev/ttyUSB1
sent [LCP ConfReq id=0x1 <asyncmap 0x0> <magic 0xc33c412b> <pcomp> <accomp>]
rcvd [LCP ConfRej id=0x1 <magic 0xc33c412b> <pcomp>]
sent [LCP ConfReq id=0x2 <asyncmap 0x0> <accomp>]
rcvd [LCP ConfAck id=0x2 <asyncmap 0x0> <accomp>]
rcvd [LCP ConfReq id=0x3 <mru 1280> <asyncmap 0x0> <accomp>]
sent [LCP ConfAck id=0x3 <mru 1280> <asyncmap 0x0> <accomp>]
sent [IPV6CP ConfReq id=0x1 <addr fe80::0000:0000:0000:0023>]
rcvd [IPV6CP ConfReq id=0x1 <addr fe80::0000:0000:0000:0000>]
sent [IPV6CP ConfNak id=0x1 <addr fe80::0000:0000:0000:0024>]
rcvd [IPV6CP ConfReq id=0x2 <addr fe80::0000:0000:0000:0000>]
sent [IPV6CP ConfNak id=0x2 <addr fe80::757d:cee8:dcad:b877>]
rcvd [IPV6CP ConfReq id=0x3 <addr fe80::0000:0000:0000:0024>]
sent [IPV6CP ConfAck id=0x3 <addr fe80::0000:0000:0000:0024>]
sent [IPV6CP ConfReq id=0x1 <addr fe80::0000:0000:0000:0023>]
rcvd [IPV6CP ConfAck id=0x1 <addr fe80::0000:0000:0000:0023>]
local  LL address fe80::0000:0000:0000:0023
remote LL address fe80::0000:0000:0000:0024
Script /etc/ppp/ipv6-up started (pid 23284)
Script /etc/ppp/ipv6-up finished (pid 23284), status = 0x0

Open a new terminal and run

sudo ifconfig ppp0 add fec0::100/64

The ppp connection is now established.

Run CoAP example client

In a new terminal execute the following commands to run the CoAP example client and request a resource from the server:

cd $TOSROOT/support/sdk/c/coap/examples
./coap-client -m get coap://[fec0::3]:61616/<URI> -t binary

whereas URI specifies the resource you want to access and the response will be in binary representation. For TelosB motes, currently the following resources are supported and can be enabled/disabled in the Makefile of the CoAP application:

Note: Due to restricted memory capacities on TelosB motes, you can not enable all resources at the same time!


 Resource   GET   PUT  Comments
 /st X - Temperature
 /sh X - Humidity
 /sv X - Voltage
 /r X - Temperature, Humidity
and Voltage
 /l X X LED's
 /ck (X) X AES Encryption Key

Note by requesting the Resource /st and /r, the CoAP message exchange turns into an asynchronous (deferred/seperate) message exchange. For this reason, a Token is required. The following command should be used instead:

./coap-client -m get coap://[fec0::3]:61616/<URI> -T <TOKEN> -t binary

where TOKEN might be any string, e.g. 3a.

For using the PUT method, the following commands can be use:

LED's:

This command sets the LED's of the mote to state 2 (OFF, ON, OFF):

echo -e -n \\x02 | ./coap-client -m put coap://[fec0::3]:61616/l -T 3a -t binary -f -

AES Encryption Key:

Since the AES key contains 25 values, the most convenient way is to store the key in a file and send its content with the PUT method to the mote. To do so, at first create the HEX file with example values by using this command:

echo -e -n \\xFF\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x10\\x11\\x12\\x13\\x14\\x15\\x16 >> file

Then send the file to the mote by executing

./coap-client -m put coap://[fec0::3]:61616/ck -T 3a -t binary -f file

You can also, implemented for testing purposes, read the key from storage by using the GET method:

./coap-client -m get coap://[fec0::3]:61616/ck -T 3a -t binary

Important parts of TinyOS CoAP

$TOSROOT/apps/CoapBlip/* Sample application
$TOSROOT/support/make/coap.extra Makefile extension for CoAP
$TOSROOT/support/sdk/c/coap/* libcoap C-Implementation
$TOSROOT/support/sdk/c/coap/example Example client and server using libcoap
$TOSROOT/tos/lib/net/coap/* Core libcoap TinyOS adaptation
$TOSROOT/tos/interfaces/ReadResource.nc
$TOSROOT/tos/interfaces/WriteResource.nc
$TOSROOT/tos/interfaces/CoAPClient.nc
$TOSROOT/tos/interfaces/CoAPServer.nc
$TOSROOT/tos/interfaces/LibCoAP.nc
CoAP interfaces

Wiring your resource to the application

Accessing resources on the mote, parameterized ReadResource and WriteResource interfaces have been introduced to provide multiple independent instances of the same interface for all available resources. This design scheme saves code space, eliminates fan-outs and allows portability to different motes by simply changing the wiring. For hardware dependent calculations of the retrieved sensor values, the CoAPBuffer{Volt|Hum|Temp}Translate components have been introduced. These components can be optional used to transform the received buffer values from the sensors to SI units using fixed-point calculations.

ReadResource

The ReadResource interface provides three functions:

interface ReadResource {
   command error_t get(coap_tid_t id); 
   event void getDone(error_t result, coap_tid_t id, uint8_t asyn_message, uint8_t* val, uint8_t buflen);
   event void getDoneDeferred(coap_tid_t id);
}

To read a resource, the get() command can be called. As parameter it takes the Message ID (id) to uniquely identify the response signaled back to the caller by getDone() or getDoneDeferred. The getDone() event is signaled if the sensor reading has finished within a predefined time (PREACK_TIMEOUT) to create a immediate response. In case of a deferred message response, i.e. sensor data retrieval takes longer as defined in PREACK_TIMEOUT, getDoneDeferred() is signaled to establish a asynchronous message exchange.

Example code to wire temperature and humidity sensor on TelosB motes:

configuration CoapBlipC {

} implementation {
   CoapBlipP.CoAPServer -> CoapUdpServerC;
   components new SensirionSht11C() as HumTempSensor;

   components new CoapReadResourceC(uint16_t, KEY_TEMP) as CoapReadTempResource;
   CoapReadTempResource.Read -> HumTempSensor.Temperature;
   CoapUdpServerC.ReadResource[KEY_TEMP] -> CoapReadTempResource.ReadResource;

   components new CoapReadResourceC(uint16_t, KEY_HUM) as CoapReadHumResource;
   CoapReadHumResource.Read -> HumTempSensor.Humidity;
   CoapUdpServerC.ReadResource[KEY_HUM] -> CoapReadHumResource.ReadResource;
}

The mapping of the parameterized ReadResource to its corresponding sensor is done by its key value which is mapped at compile-time to a constant integer by a predefined mapping function (get_key()) in tinyos_coap_ressources.h. Here the key "KEY_TEMP" corresponds to a 0 whereas the key "KEY_HUM" is mapped to 1.

Requesting data from a sensor, the following command needs to be called:

call ReadResource.get[get_key(uri->path.s, uri->path.length)](*id);

In case of an inappropriate call of get() in the application, e.g. for a non existing URI, the default function below must be implemented in the application to handle this exception:

default command error_t ReadResource.get[uint16_t uri_key](coap_tid_t id) {
 //get not available for this resource, handle this case
 return FAIL;
}

After the sensor is read, getDone() is signaled and returns the sensor data. The following function handles this event:

event void ReadResource.getDone[uint16_t uri_key](error_t result,
                                                  coap_tid_t id,
                                                  uint8_t asyn_message,
                                                  uint8_t* val_buf,
                                                  uint8_t buflen) {

//send response with sensor value
}

The same implementation holds for the getDoneDeferred() event.

WriteResource

Since the LED resource, for example, provides both, Read- and Write-Resource interfaces, another component (CoapLedResource) is used to provide full access to the LedsC module. While ReadResource provides the get() command, the WriteResource provides the put() command to modify or change the state of a resource.

The interface looks as follows:

interface WriteResource {
   command error_t put(uint8_t *val, uint8_t buflen, coap_tid_t id);
   event void putDone(error_t result, coap_tid_t id, uint8_t asyn_message);
   event void putDoneDeferred(coap_tid_t id);
}

For accessing the LED resource, the wiring looks like this:

configuration CoapBlipC {

} implementation {
   CoapBlipP.CoAPServer -> CoapUdpServerC;
   CoapBlipP.Leds -> LedsC;

   components new CoapLedResourceC(KEY_LED) as CoapLedResource;
   CoapLedResource.Leds -> LedsC;
   CoapUdpServerC.ReadResource[KEY_LED]  -> CoapLedResource.ReadResource;
   CoapUdpServerC.WriteResource[KEY_LED] -> CoapLedResource.WriteResource;
}

Accessing the WriteResource out of the application, the put() function as well as its default command has to be implemented.

call WriteResource.put[get_key(uri->path.s, uri->path.length)](buf, *buflen, *id)

default command error_t WriteResource.put[uint8_t uri_key](uint8_t* val, uint8_t buflen, coap_tid_t id) {
//put not available for this resource, handle this case
return FAIL;
}

putDoneDeferred is currently not implemented but might be used in the future.

CoAPBuffer{Volt|Hum|Temp}Translate

These components are used for calculations of the corresponding SI unit of the received sensor values. By providing and using the Read interface, they can easily be connected between the CoapReadResource and the sensor components to calculate fix-point values (real SI value * 100) of the respective sensor value. These components are hardware dependent and have to be adapted when changing hardware and/or sensors.

For using the Translate components for temperature and humidity, change the above given configuration to

configuration CoapBlipC {

} implementation {
   CoapBlipP.CoAPServer -> CoapUdpServerC;
   components new SensirionSht11C() as HumTempSensor;

   components new CoapReadResourceC(uint16_t, KEY_TEMP) as CoapReadTempResource;
   components new CoapBufferTempTranslateC() as CoapBufferTempTranslate;
   CoapReadTempResource.Read -> CoapBufferTempTranslate.ReadTemp;
   CoapBufferTempTranslate.Read -> HumTempSensor.Temperature;
   CoapUdpServerC.ReadResource[KEY_TEMP] -> CoapReadTempResource.ReadResource;ce;

   components new CoapReadResourceC(uint16_t, KEY_HUM) as CoapReadHumResource;
   components new CoapBufferHumTranslateC() as CoapBufferHumTranslate;
   CoapReadHumResource.Read -> CoapBufferHumTranslate.ReadHum;
   CoapBufferHumTranslate.Read -> HumTempSensor.Humidity;
   CoapUdpServerC.ReadResource[KEY_HUM] -> CoapReadHumResource.ReadResource;ce;
}

The graph below shows the structure of the CoAP application with the above given wiring.

CoapBlipC doc.png


Further information

Some more information can be found in the following publication:

Koojana Kuladinithi, Olaf Bergmann, Thomas Pötsch, Markus Becker & Carmelita Görg: Implementation of CoAP and its Application in Transport Logistics. In Proc. Of ‘Extending the Internet to Low power and Lossy Networks’ (IP+SN 2011). Chicago, USA. 11th of April 2011. Paper Presentation Slides