Having to run a piece of technology on a memory-constrained device changes everything. Web server technology is primarily meant to run on big servers, capable of serving a large number of concurrent clients, with complex data being transported over secure and/or reliable TCP sockets. HTTP is the de-facto protocol for interacting with such servers. Then there are embedded devices with RAM as low as 2kb.

Some ambitious people tried to turn constrained devices into HTTP servers in order to publish their sensor data to clients. It was obviously a bad idea, if you know what it takes to implement a full-fledged HTTP server. It gets worse if the "clients" are actually other constrained devices. It gets even worse if these devices are on a lossy network. Some of these people were smart enough to propose a replacement for HTTP for these kind of cases.

Enter, Constrained Application Protocol (CoAP). With CoAP, constrained devices can implement HTTP verbs, like GET, POST etc., and get away without implementing a full HTTP server. First, CoAP is designed to run on top of UDP, which considerably simplifies the network stack. There is support for TCP as well but lets forget it exists for now. Second, its message components are all binary, which means very low protocol bytes overhead and extremely simple-to-parse packets.

Packet Format

Lets compare a smallest possible HTTP GET vs a CoAP GET message:

HTTP: GET / HTTP/1.1\r\nHost: hostaddress\r\n\r\n
CoAP: 0x51 0x01 0xab 0xcd 0xef

HTTP needs 27 bytes, excluding hostaddress, versus 5 bytes for CoAP. It is already apparent that CoAP messages are not meant to be human-readable, unlike HTTP. It is harder to construct a CoAP message by hand. Your constrained devices, on the other hand, will happily encode/decode these messages. Here's a quick breakdown of the CoAP message.

0x51   : 01   | 01           | 0001
         --------------------------------
         ver. | NON msg type | token len.
         
0x01   : 000     | 00001
         ---------------
         request | GET
         
0xabcd : message ID

0xef   : token

For the curious, the full RFC is here.

Reliability over UDP

As mentioned earlier, CoAP uses UDP as the transport, so the missing reliability mechanism is built into CoAP itself. Looking at the above CoAP example message, the message type is NON or non-confirmable. NON messages are fire-and-forget kind of messages, where the sender doesn't expect an acknowledgement from the receiver. Any response sent back contains the token that was originally sent, so the sender still needs to keep the sent tokens temporarily to match with responses, if there are any. Another kind of message type is CON or confirmable. These messages expect an ACK type of message as response from the server, and are repeatedly sent until they get the acknowledgement. Message IDs are used to match the sent message with their ACK responses, so the sender needs to keep IDs for CON messages in memory.

Responses to requests of type CON are normally CON themselves. Which means the original requester needs to send back an ACK after receiving a response. NON responses are usually sent for NON requests, but can be CON.

Resource Discovery

A very neat feature of CoAP is the discovery of all services offered by a server. Anyone (or anything) can call GET /.well-known/core to get a list of URIs that can be accessed. Here is an example resource discovery transaction.

GET /.well-known/core

</sensors/temp>;rt="temperature-c";if="sensor",
</sensors/light>;rt="light-lux";if="sensor"

This server offers two URIs. If the response parser knows how to interpret resources of types temperature-c and light-lux, and knows what verbs are allowed on interface sensor, it can fully interact with the server. This simple resource discovery allows machine-to-machine client/server communication model.

Simplicity comes at a cost. Before even being part of a system, all communicating entities have to agree upon and have full knowledge of all resource types and interfaces involved in the system, which is quite limiting. There's not much you can do to "filter" your resource discovery either. For example, you can't query "a list of all temperature sensors from floor 8" using standard methods. There is still a large room for improvement in CoAP's resource discovery process.

There is a whole RFC for resource discovery alone here

I am currently working on a better resource discovery process, which I'll be posting about here. Stay tuned!