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
		}
	}
Revision:
1:5eec2844ad47
Parent:
0:3d62a105fd34
--- a/cantcoap.cpp	Tue Oct 08 14:36:01 2013 +0000
+++ b/cantcoap.cpp	Thu Jan 30 14:07:56 2014 +0000
@@ -36,12 +36,26 @@
 #include <stdlib.h>
 #include <stdint.h>
 #include <string.h>
+#include <inet.h>
 #include "cantcoap.h"
-#ifndef MBED
-#include "arpa/inet.h"
-#include "netdb.h"
+
+// for debugging output you need https://mbed.org/users/donatien/code/DebugLib/
+#define __DEBUG__ 3 // INFO
+#ifndef __MODULE__
+   #define __MODULE__ "cantcoap.cpp"
+#endif
+#include "dbg.h"
+// some extra debug stuff
+#if __DEBUG__ > 0
+   #define INFOX(...)  do { fprintf(stderr,__VA_ARGS__); } while(0)
+   #define INFOLX(...) do { fprintf(stderr,"[INFO] %s:%d ",__MODULE__,__LINE__); fprintf(stderr,__VA_ARGS__); } while(0) 
+   #define DBGLX(...)  do { fprintf(stderr,"[DBG] %s:%d ",__MODULE__,__LINE__); fprintf(stderr,__VA_ARGS__); } while(0)
+   #define DBG_PDU()   do { printBin(); } while(0)
 #else
-#include "bsd_socket.h"
+   #define INFOX(...)  do{} while(0)
+   #define INFOLX(...) do{} while(0)
+   #define DBGLX(...)  do{} while(0)
+   #define DBG_PDU()   do{} while(0)
 #endif
 
 
@@ -108,11 +122,25 @@
  * \sa CoapPDU::CoapPDU(), CoapPDU::CoapPDU(uint8_t *buffer, int bufferLength, int pduLength)
  */
 CoapPDU::CoapPDU(uint8_t *pdu, int pduLength) {
+    int bufferLength = pduLength;
+    // sanity
+	if(pduLength<4&&pduLength!=0) {
+		DBG("PDU cannot have a length less than 4");
+	}
+
 	// pdu
 	_pdu = pdu;
-	_bufferLength = pduLength;
-	_pduLength = pduLength;
-	
+	_bufferLength = bufferLength;
+	if(pduLength==0) {
+		// this is actually a fresh pdu, header always exists
+		_pduLength = 4;
+		// make sure header is zeroed
+		_pdu[0] = 0x00; _pdu[1] = 0x00; _pdu[2] = 0x00; _pdu[3] = 0x00;
+		setVersion(1);
+	} else {
+		_pduLength = pduLength;
+	}
+
 	_constructedFromBuffer = 1;
 
 	// options
@@ -419,6 +447,13 @@
 	_pduLength = len;
 }
 
+/// Shorthand function for setting a resource URI.
+/**
+ * Calls CoapPDU::setURI(uri,strlen(uri).
+ */
+int CoapPDU::setURI(char *uri) {
+	return setURI(uri,strlen(uri));
+}
 
 /// Shorthand function for setting a resource URI.
 /**
@@ -513,10 +548,21 @@
 	free(uriBuf);
 	return 0;
 }
+/// Shorthand for adding a URI QUERY to the option list.
+/**
+ * Adds a new option to the CoAP PDU that encodes a URI_QUERY.
+ *
+ * \param query The uri query to encode.
+ * \return 0 on success, 1 on failure.
+ */
+int CoapPDU::addURIQuery(char *query) {
+	return addOption(COAP_OPTION_URI_QUERY,strlen(query),(uint8_t*)query);
+}
 
 /// Concatenates any URI_PATH elements into a single string.
 /**
  * Parses the PDU options and extracts all URI_PATH elements, concatenating them into a single string with slash separators.
+ * The produced string will be null terminated.
  * 
  * \param dst Buffer into which to copy the concatenated path elements.
  * \param dstlen Length of buffer.
@@ -785,6 +831,71 @@
 CoapPDU::Code CoapPDU::getCode() {
 	return (CoapPDU::Code)_pdu[1];
 }
+
+
+/// Converts a http status code as an integer, to a CoAP code.
+/**
+ * \param httpStatus the HTTP status code as an integer (e.g 200)
+ * \return The correct corresponding CoapPDU::Code on success,
+ * CoapPDU::COAP_UNDEFINED_CODE on failure.
+ */
+CoapPDU::Code CoapPDU::httpStatusToCode(int httpStatus) {
+	switch(httpStatus) {
+		case 1:
+			return CoapPDU::COAP_GET;
+		case 2:
+			return CoapPDU::COAP_POST;
+		case 3:
+			return CoapPDU::COAP_PUT;
+		case 4:
+			return CoapPDU::COAP_DELETE;
+		case 201:
+			return CoapPDU::COAP_CREATED;
+		case 202:
+			return CoapPDU::COAP_DELETED;
+		case 203:
+			return CoapPDU::COAP_VALID;
+		case 204:
+			return CoapPDU::COAP_CHANGED;
+		case 205:
+			return CoapPDU::COAP_CONTENT;
+		case 400:
+			return CoapPDU::COAP_BAD_REQUEST;
+		case 401:
+			return CoapPDU::COAP_UNAUTHORIZED;
+		case 402:
+			return CoapPDU::COAP_BAD_OPTION;
+		case 403:
+			return CoapPDU::COAP_FORBIDDEN;
+		case 404:
+			return CoapPDU::COAP_NOT_FOUND;
+		case 405:
+			return CoapPDU::COAP_METHOD_NOT_ALLOWED;
+		case 406:
+			return CoapPDU::COAP_NOT_ACCEPTABLE;
+		case 412:
+			return CoapPDU::COAP_PRECONDITION_FAILED;
+		case 413:
+			return CoapPDU::COAP_REQUEST_ENTITY_TOO_LARGE;
+		case 415:
+			return CoapPDU::COAP_UNSUPPORTED_CONTENT_FORMAT;
+		case 500:
+			return CoapPDU::COAP_INTERNAL_SERVER_ERROR;
+		case 501:
+			return CoapPDU::COAP_NOT_IMPLEMENTED;
+		case 502:
+			return CoapPDU::COAP_BAD_GATEWAY;
+		case 503:
+			return CoapPDU::COAP_SERVICE_UNAVAILABLE;
+		case 504:
+			return CoapPDU::COAP_GATEWAY_TIMEOUT;
+		case 505:
+			return CoapPDU::COAP_PROXYING_NOT_SUPPORTED;
+		default:
+			return CoapPDU::COAP_UNDEFINED_CODE;
+	}
+}
+
 /// Set messageID to the supplied value.
 /**
  * \param messageID A 16bit message id.
@@ -1452,200 +1563,205 @@
 
 /// Prints the PDU in human-readable format.
 void CoapPDU::printHuman() {
-	INFO("__________________");
+	INFOX("__________________\r\n");
 	if(_constructedFromBuffer) {
-		INFO("PDU was constructed from buffer of %d bytes",_bufferLength);
+		INFOX("PDU was constructed from buffer of %d bytes\r\n",_bufferLength);
 	}
-	INFO("PDU is %d bytes long",_pduLength);
-	INFO("CoAP Version: %d",getVersion());
+	INFOX("PDU is %d bytes long\r\n",_pduLength);
+	INFOX("CoAP Version: %d\r\n",getVersion());
 	INFOX("Message Type: ");
 	switch(getType()) {
 		case COAP_CONFIRMABLE:
-			INFO("Confirmable");
+			INFOX("Confirmable\r\n");
 		break;
 
 		case COAP_NON_CONFIRMABLE:
-			INFO("Non-Confirmable");
+			INFOX("Non-Confirmable\r\n");
 		break;
 
 		case COAP_ACKNOWLEDGEMENT:
-			INFO("Acknowledgement");
+			INFOX("Acknowledgement\r\n");
 		break;
 
 		case COAP_RESET:
-			INFO("Reset");
+			INFOX("Reset\r\n");
 		break;
 	}
-	INFO("Token length: %d",getTokenLength());
+	INFOX("Token length: %d\r\n",getTokenLength());
 	INFOX("Code: ");
 	switch(getCode()) {
 		case COAP_EMPTY:
-			INFO("0.00 Empty");
+			INFOX("0.00 Empty");
 		break;
 		case COAP_GET:
-			INFO("0.01 GET");
+			INFOX("0.01 GET");
 		break;
 		case COAP_POST:
-			INFO("0.02 POST");
+			INFOX("0.02 POST");
 		break;
 		case COAP_PUT:
-			INFO("0.03 PUT");
+			INFOX("0.03 PUT");
 		break;
 		case COAP_DELETE:
-			INFO("0.04 DELETE");
+			INFOX("0.04 DELETE");
 		break;
 		case COAP_CREATED:
-			INFO("2.01 Created");
+			INFOX("2.01 Created");
 		break;
 		case COAP_DELETED:
-			INFO("2.02 Deleted");
+			INFOX("2.02 Deleted");
 		break;
 		case COAP_VALID:
-			INFO("2.03 Valid");
+			INFOX("2.03 Valid");
 		break;
 		case COAP_CHANGED:
-			INFO("2.04 Changed");
+			INFOX("2.04 Changed");
 		break;
 		case COAP_CONTENT:
-			INFO("2.05 Content");
+			INFOX("2.05 Content");
 		break;
 		case COAP_BAD_REQUEST:
-			INFO("4.00 Bad Request");
+			INFOX("4.00 Bad Request");
 		break;
 		case COAP_UNAUTHORIZED:
-			INFO("4.01 Unauthorized");
+			INFOX("4.01 Unauthorized");
 		break;
 		case COAP_BAD_OPTION:
-			INFO("4.02 Bad Option");
+			INFOX("4.02 Bad Option");
 		break;
 		case COAP_FORBIDDEN:
-			INFO("4.03 Forbidden");
+			INFOX("4.03 Forbidden");
 		break;
 		case COAP_NOT_FOUND:
-			INFO("4.04 Not Found");
+			INFOX("4.04 Not Found");
 		break;
 		case COAP_METHOD_NOT_ALLOWED:
-			INFO("4.05 Method Not Allowed");
+			INFOX("4.05 Method Not Allowed");
 		break;
 		case COAP_NOT_ACCEPTABLE:
-			INFO("4.06 Not Acceptable");
+			INFOX("4.06 Not Acceptable");
 		break;
 		case COAP_PRECONDITION_FAILED:
-			INFO("4.12 Precondition Failed");
+			INFOX("4.12 Precondition Failed");
 		break;
 		case COAP_REQUEST_ENTITY_TOO_LARGE:
-			INFO("4.13 Request Entity Too Large");
+			INFOX("4.13 Request Entity Too Large");
 		break;
 		case COAP_UNSUPPORTED_CONTENT_FORMAT:
-			INFO("4.15 Unsupported Content-Format");
+			INFOX("4.15 Unsupported Content-Format");
 		break;
 		case COAP_INTERNAL_SERVER_ERROR:
-			INFO("5.00 Internal Server Error");
+			INFOX("5.00 Internal Server Error");
 		break;
 		case COAP_NOT_IMPLEMENTED:
-			INFO("5.01 Not Implemented");
+			INFOX("5.01 Not Implemented");
 		break;
 		case COAP_BAD_GATEWAY:
-			INFO("5.02 Bad Gateway");
+			INFOX("5.02 Bad Gateway");
 		break;
 		case COAP_SERVICE_UNAVAILABLE:
-			INFO("5.03 Service Unavailable");
+			INFOX("5.03 Service Unavailable");
 		break;
 		case COAP_GATEWAY_TIMEOUT:
-			INFO("5.04 Gateway Timeout");
+			INFOX("5.04 Gateway Timeout");
 		break;
 		case COAP_PROXYING_NOT_SUPPORTED:
-			INFO("5.05 Proxying Not Supported");
+			INFOX("5.05 Proxying Not Supported");
+		break;
+		case COAP_UNDEFINED_CODE:
+			INFOX("Undefined Code");
 		break;
 	}
+	INFOX("\r\n");
 
 	// print message ID
-	INFO("Message ID: %u",getMessageID());
+	INFOX("Message ID: %u\r\n",getMessageID());
 
 	// print token value
 	int tokenLength = getTokenLength();
 	uint8_t *tokenPointer = getPDUPointer()+COAP_HDR_SIZE;
 	if(tokenLength==0) {
-		INFO("No token.");
+		INFOX("No token.\r\n");
 	} else {
-		INFO("Token of %d bytes.",tokenLength);
+		INFOX("Token of %d bytes.\r\n",tokenLength);
 		INFOX("   Value: 0x");
 		for(int j=0; j<tokenLength; j++) {
 			INFOX("%.2x",tokenPointer[j]);
 		}
-		INFO(" ");
+		INFOX("\r\n");
 	}
 
 	// print options
 	CoapPDU::CoapOption* options = getOptions();
-	INFO("%d options:",_numOptions);
+	INFOX("%d options:\r\n",_numOptions);
 	for(int i=0; i<_numOptions; i++) {
-		INFO("OPTION (%d/%d)",i,_numOptions);
-		INFO("   Option number (delta): %hu (%hu)",options[i].optionNumber,options[i].optionDelta);
+		INFOX("OPTION (%d/%d)\r\n",i,_numOptions);
+		INFOX("   Option number (delta): %hu (%hu)\r\n",options[i].optionNumber,options[i].optionDelta);
 		INFOX("   Name: ");
 		switch(options[i].optionNumber) {
 			case COAP_OPTION_IF_MATCH:
-				INFO("IF_MATCH");
+				INFOX("IF_MATCH");
 			break;
 			case COAP_OPTION_URI_HOST:
-				INFO("URI_HOST");
+				INFOX("URI_HOST");
 			break;
 			case COAP_OPTION_ETAG:
-				INFO("ETAG");
+				INFOX("ETAG");
 			break;
 			case COAP_OPTION_IF_NONE_MATCH:
-				INFO("IF_NONE_MATCH");
+				INFOX("IF_NONE_MATCH");
 			break;
 			case COAP_OPTION_OBSERVE:
-				INFO("OBSERVE");
+				INFOX("OBSERVE");
 			break;
 			case COAP_OPTION_URI_PORT:
-				INFO("URI_PORT");
+				INFOX("URI_PORT");
 			break;
 			case COAP_OPTION_LOCATION_PATH:
-				INFO("LOCATION_PATH");
+				INFOX("LOCATION_PATH");
 			break;
 			case COAP_OPTION_URI_PATH:
-				INFO("URI_PATH");
+				INFOX("URI_PATH");
 			break;
 			case COAP_OPTION_CONTENT_FORMAT:
-				INFO("CONTENT_FORMAT");
+				INFOX("CONTENT_FORMAT");
 			break;
 			case COAP_OPTION_MAX_AGE:
-				INFO("MAX_AGE");
+				INFOX("MAX_AGE");
 			break;
 			case COAP_OPTION_URI_QUERY:
-				INFO("URI_QUERY");
+				INFOX("URI_QUERY");
 			break;
 			case COAP_OPTION_ACCEPT:
-				INFO("ACCEPT");
+				INFOX("ACCEPT");
 			break;
 			case COAP_OPTION_LOCATION_QUERY:
-				INFO("LOCATION_QUERY");
+				INFOX("LOCATION_QUERY");
 			break;
 			case COAP_OPTION_PROXY_URI:
-				INFO("PROXY_URI");
+				INFOX("PROXY_URI");
 			break;
 			case COAP_OPTION_PROXY_SCHEME:
-				INFO("PROXY_SCHEME");
+				INFOX("PROXY_SCHEME");
 			break;
 			case COAP_OPTION_BLOCK1:
-				INFO("BLOCK1");
+				INFOX("BLOCK1");
 			break;
 			case COAP_OPTION_BLOCK2:
-				INFO("BLOCK2");
+				INFOX("BLOCK2");
 			break;
 			case COAP_OPTION_SIZE1:
-				INFO("SIZE1");
+				INFOX("SIZE1");
 			break;
 			case COAP_OPTION_SIZE2:
-				INFO("SIZE2");
+				INFOX("SIZE2");
 			break;
 			default:
-				INFO("Unknown option");
+				INFOX("Unknown option");
 			break;
 		}
-		INFO("   Value length: %u",options[i].optionValueLength);
+		INFOX("\r\n");
+		INFOX("   Value length: %u\r\n",options[i].optionValueLength);
 		INFOX("   Value: \"");
 		for(int j=0; j<options[i].optionValueLength; j++) {
 			char c = options[i].optionValuePointer[j];
@@ -1655,14 +1771,14 @@
 				INFOX("\\%.2d",c);
 			}
 		}
-		INFO("\"");
+		INFOX("\"\r\n");
 	}
 	
 	// print payload
 	if(_payloadLength==0) {
-		INFO("No payload.");
+		INFOX("No payload.\r\n");
 	} else {
-		INFO("Payload of %d bytes",_payloadLength);
+		INFOX("Payload of %d bytes\r\n",_payloadLength);
 		INFOX("   Value: \"");
 		for(int j=0; j<_payloadLength; j++) {
 			char c = _payloadPointer[j];
@@ -1672,9 +1788,9 @@
 				INFOX("\\%.2x",c);
 			}
 		}
-		INFO("\"");
+		INFO("\"\r\n");
 	}
-	INFO("__________________");
+	INFOX("__________________\r\n");
 }
 
 /// Prints the PDU as a c array (useful for debugging or hardcoding PDUs)
@@ -1795,4 +1911,4 @@
 /// Dumps the PDU as a byte sequence to stdout.
 void CoapPDU::print() {
 	fwrite(_pdu,1,_pduLength,stdout);
-}
+}
\ No newline at end of file