This is CoAP library with a focus on simplicity. It offers minimal CoAP PDU construction and decoding to and from byte buffers.
Dependents: cantcoap_cellular_test yeswecancoap
Teaser
CoAP implementation that focuses on simplicity by offering a minimal set of functions and straightforward interface.
CoapPDU *pdu = new CoapPDU(); pdu->setType(CoapPDU::COAP_CONFIRMABLE); pdu->setCode(CoapPDU::COAP_GET); pdu->setToken((uint8_t*)"\3\2\1\0",4); pdu->setMessageID(0x0005); pdu->setURI((char*)"test",4); // send packet ret = send(sockfd,pdu->getPDUPointer(),pdu->getPDULength(),0);
...
// receive packet ret = recvfrom(sockfd,&buffer,BUF_LEN,0,(sockaddr*)&recvAddr,&recvAddrLen); CoapPDU *recvPDU = new CoapPDU((uint8_t*)buffer,ret); if(recvPDU->validate()) { recvPDU->getURI(uriBuffer,URI_BUF_LEN,&recvURILen); ... }
Long description
This is a CoAP implementation with a focus on simplicity. The library only provides PDU construction and de-construction.
The user is expected to deal with retransmissions, timeouts, and message ID matching themselves. This isn’t as arduous as it sounds and makes a lot more sense on a constrained device.
Imagine for example a simple microcontroller sensor that only reports readings once every 15 minutes, and only sends a few packets each time. Do you really need a complicated framework to deal with acknowledgements and re-transmissions?
Since CoAP recommends you only send one packet at at time, this means you only need to keep track of one on-going transaction at a time. Yeah... I think you’re capable of this.
Furthermore, the timers and interrupt processes between different embedded processor architectures, vary quite a bit. So it often makes sense to write the packet sending processes yourself.
Finally, you might be sending the packets over odd transport bearers such as a SMS (woah dude, that's just totally wild) or a simple radio bearer. In which case, it’s easiest to deal with buffers. If I built retransmission handlers, they’d all be UDP/IP specific and would bloat the code for no reason.
Resources
Github page for the cantcoap library is here:
https://github.com/staropram/cantcoap
Doxygen for the library is here:
http://staropram.github.io/cantcoap/index.html
Examples
Construction
There are a couple of different ways to construct a PDU depending on whether you want the library to allocate memory for you, or whether you have an external buffer you want to use. You can also re-purpose existing objects.
Using a managed object
The simplest usage scenario hands control of memory allocation to the library:
CoapPDU *pdu = new CoapPDU(); ... pdu->setType(CoapPDU::COAP_CONFIRMABLE); pdu->setCode(CoapPDU::COAP_GET); pdu->addOption(11,5,(uint8_t*)"hello"); pdu->addOption(11,5,(uint8_t*)"there"); pdu->addOption(11,6,(uint8_t*)"server");
In this case you just call the default constructor. That's it. The library handles memory from there-on out. For example, when adding each of those options, the library will realloc the pdu to accomodate space for them. It will also shrink the PDU if something changes (like the token length) so that it always uses the minimum amount of memory.
When you free the PDU, all data including the buffer is deleted. The PDU can also be reused as shown below.
Using an external buffer for memory
There are two obvious reasons why you would do this:
1. The buffer contains a CoAP PDU and you want to access the data in the PDU. 2. Buffers cost space and allocating memory consumes processor resources. On embedded targets it is often simpler to reuse buffers where possible.
The first instance is a special case and requires some extra work. Just using an external buffer is as simple as follows:
uint8_t *buffer[100]; CoapPDU *pdu = new CoapPDU((uint8_t*)buffer,100,0); ... pdu->setType(CoapPDU::COAP_CONFIRMABLE); pdu->setCode(CoapPDU::COAP_GET); pdu->addOption(11,5,(uint8_t*)"hello"); pdu->addOption(11,5,(uint8_t*)"there"); pdu->addOption(11,6,(uint8_t*)"server");
The PDU is constructed as normal except that the memory of your buffer is used instead of allocated memory.
A call such as this:
pdu->addOption(11,5,(uint8_t*)"hello");
Will fail if there is no space left in the buffer.
When you delete the object, the buffer is not freed. Hey, it's your buffer mannn!
Reusing an existing object
Regardless of whether you constructed a PDU using either of the above methods, you can always reuse it:
pdu->reset(); ... pdu->setType(CoapPDU::COAP_CONFIRMABLE); pdu->setCode(CoapPDU::COAP_GET); pdu->addOption(11,5,(uint8_t*)"hello"); pdu->addOption(11,5,(uint8_t*)"there"); pdu->addOption(11,6,(uint8_t*)"server");
The only difference is that if the PDU was initially constructed using managed-memory, then it will continue to have managed-memory. Whereas if the PDU was constructed with an external buffer, then you are limited in space by the size of the buffer you used.
Receving CoAP packets over a network or something
In this case you have a CoAP PDU in a buffer you just gobbled from a socket and want to read it:
uint8_t *buffer[100]; int ret = recvfrom(sockfd,&buffer,BUF_LEN,0,(sockaddr*)&recvAddr,&recvAddrLen); CoapPDU *recvPDU = new CoapPDU((uint8_t*)buffer,ret,100); if(recvPDU->validate()) { recvPDU->printHuman(); // do your work }
You must call CoapPDU::validate() and get a positive response before accessing any of the data members. This sets up some internal pionters and so on, so if you fail to do it, undefined behaviour will result.
Note that the constructor is just a shorthand for the external-buffer-constructor explained above, and you can use the long form if you want. For example. you might want to use the long form if you have a buffer bigger than the PDU and you expect to reuse it.
You can reuse this object by resetting it as above.
If you reuse such an object, you need to set the PDU length manually because there is no way to deduce the PDU length using validate():
// earlier #define BUFLEN 500 char buffer[BUFLEN]; CoapPDU *recvPDU = new CoapPDU((uint8_t*)buffer,BUFLEN,BUFLEN); ... while(1) { // receive packet ret = sockfd,&buffer,BUFLEN,0); if(ret==-1) { INFO("Error receiving data"); // handle error } // validate packet // you should also check that ret doesn't exceed buffer length recvPDU->setPDULength(ret); if(recvPDU->validate()!=1) { INFO("Malformed CoAP packet"); // handle error } }
Diff: cantcoap.h
- Revision:
- 1:5eec2844ad47
- Parent:
- 0:3d62a105fd34
diff -r 3d62a105fd34 -r 5eec2844ad47 cantcoap.h --- a/cantcoap.h Tue Oct 08 14:36:01 2013 +0000 +++ b/cantcoap.h Thu Jan 30 14:07:56 2014 +0000 @@ -1,25 +1,31 @@ -/// Copyright (c) 2013, Ashley Mills. -#define MBED -#ifndef MBED -#include <unistd.h> -#endif -#include <stdint.h> +/** +Copyright (c) 2013, Ashley Mills. +All rights reserved. -#define INFO(...) printf(__VA_ARGS__); printf("\r\n"); -#define INFOX(...); printf(__VA_ARGS__); -#define ERR(...) printf(__VA_ARGS__); printf("\r\n"); +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. -#ifdef DEBUG - #define DBG(...) fprintf(stderr,"%s:%d ",__FILE__,__LINE__); fprintf(stderr,__VA_ARGS__); fprintf(stderr,"\r\n"); - #define DBGX(...) fprintf(stderr,__VA_ARGS__); - #define DBGLX(...) fprintf(stderr,"%s:%d ",__FILE__,__LINE__); fprintf(stderr,__VA_ARGS__); - #define DBG_PDU() printBin(); -#else - #define DBG(...) {}; - #define DBGX(...) {}; - #define DBGLX(...) {}; - #define DBG_PDU() {}; -#endif +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include <stdint.h> #define COAP_HDR_SIZE 4 #define COAP_OPTION_HDR_BYTE 1 @@ -77,7 +83,8 @@ COAP_BAD_GATEWAY, COAP_SERVICE_UNAVAILABLE, COAP_GATEWAY_TIMEOUT, - COAP_PROXYING_NOT_SUPPORTED + COAP_PROXYING_NOT_SUPPORTED, + COAP_UNDEFINED_CODE=0xFF }; /// CoAP option numbers. @@ -148,6 +155,7 @@ // message code void setCode(CoapPDU::Code code); CoapPDU::Code getCode(); + CoapPDU::Code httpStatusToCode(int httpStatus); // message ID int setMessageID(uint16_t messageID); @@ -159,8 +167,10 @@ CoapOption* getOptions(); int getNumOptions(); // shorthand helpers + int setURI(char *uri); int setURI(char *uri, int urilen); int getURI(char *dst, int dstlen, int *outLen); + int addURIQuery(char *query); // content format helper int setContentFormat(CoapPDU::ContentFormat format); @@ -251,4 +261,4 @@ #define COAP_CODE_SERVICE_UNAVAILABLE 0xA3 #define COAP_CODE_GATEWAY_TIMEOUT 0xA4 #define COAP_CODE_PROXYING_NOT_SUPPORTED 0xA5 -*/ +*/ \ No newline at end of file