CoAP -03
TinyOS CoAP
Contents
[hide]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
The CoAP code might make it into the tinyos-main SVN repository soon. These instructions are still for the independent code repository.
Get the code
The CoAP code is part of the tinyos-main SVN repository from revision r5622.
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/apps/CoapBlip/libcoap git checkout -b tinyos-support-binary-output origin/tinyos-support-binary-output 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
Setup IPBasestation and IPv6 ip-driver
To install the IPBaseStation application for IPv6 support on the second attached mote, run the following set of commands.
cd $TOSROOT/apps/IPBaseStation make telosb blip install bsl,/dev/ttyUSB1
Next, build the routing driver. First, create the required TinyOS serial library by doing the following steps:
cd $TOSROOT/support/sdk/c/sf ./bootstrap ./configure make
Then for building the driver run
cd $TOSROOT/support/sdk/c/blip ./bootstrap.sh ./configure make
Now, the ip-driver has to be executed to establish the tunnel between the host an the mote
sudo ./driver/ip-driver /dev/ttyUSB1 telosb
After entering the sudo password, a similar output should be printed on your screen:
2011-02-14T18:03:00.323CET: INFO: Read config from 'serial_tun.conf' 2011-02-14T18:03:00.323CET: INFO: Using channel 16 2011-02-14T18:03:00.323CET: INFO: Retries: 5 2011-02-14T18:03:00.323CET: INFO: telnet console server running on port 6106 2011-02-14T18:03:00.325CET: INFO: created tun device: tun0 2011-02-14T18:03:00.609CET: INFO: interface device successfully initialized 2011-02-14T18:03:00.609CET: INFO: starting radvd on device tun0 2011-02-14T18:03:01.652CET: INFO: Starting to proxy for 0x1
The ip-driver has been setup successfully and you can now start the CoAP client.
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/apps/CoapBlip/libcoap/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:
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) 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/tos/lib/app/coap/* | Core libcoap TinyOS adaptation | |||||
|
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.
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