CoAP is actually more like a UDP-based HTTP for the extreme edge, designed to be small and efficient enough for devices that barely have a byte to spare.

Let’s see CoAP in action with a simple example. Imagine a temperature sensor (thermo-dev) that needs to report its readings to a server (coap-server).

First, the sensor needs to be running a CoAP server itself, listening for requests. Here’s a snippet of what that might look like in Python using aiocoap:

import asyncio
import aiocoap.resource as resource
import aiocoap

# Resource to represent the temperature sensor
class TemperatureResource(resource.Resource):
    def __init__(self):
        super().__init__()
        self.current_temp = 25.0 # Default temperature

    async def render_get(self, request):
        print(f"Received GET request, returning temperature: {self.current_temp}°C")
        return aiocoap.Message(payload=f"{self.current_temp:.1f}".encode('ascii'))

    async def render_put(self, request):
        new_temp = float(request.payload.decode('ascii'))
        print(f"Received PUT request, setting temperature to: {new_temp}°C")
        self.current_temp = new_temp
        return aiocoap.Message(code=aiocoap.responses.CHANGED,
                               payload=f"Temperature set to {self.current_temp:.1f}°C".encode('ascii'))

async def main():
    # Resource tree setup
    root = resource.Site()
    root.add_resource('temp', TemperatureResource())

    # Create CoAP server
    context = await aiocoap.Context.create_server_context(root, bind=('0.0.0.0', 5683))
    print("CoAP server started on port 5683")

    await asyncio.sleep(1000) # Keep the server running

if __name__ == "__main__":
    asyncio.run(main())

Now, on the server side (or any client wanting to interact with the sensor), we can send CoAP requests. Let’s use the coap-client command-line tool (often part of coap-cli or similar packages).

To get the temperature:

coap-client -m get coap://[::1]/temp

You’d see output like:

2.05 GET /temp
{
  "payload": "25.0"
}

To set the temperature (e.g., to 30.5°C):

coap-client -m put -e "30.5" coap://[::1]/temp

And the server would respond:

2.04 PUT /temp
{
  "payload": "Temperature set to 30.5°C"
}

This interaction is fundamental. The client sends a request (GET, PUT, POST, DELETE) to a URI on the server. The server processes it and sends back a response. CoAP uses UDP, which means requests and responses are datagrams. To handle unreliable networks, CoAP builds in reliability mechanisms: acknowledgments (ACKs) for confirmable messages and retransmissions. It also supports observe (OBSERVE) functionality, where a client can subscribe to a resource and receive updates when it changes, similar to HTTP’s Server-Sent Events or WebSockets, but much lighter.

The core problem CoAP solves is enabling IP-based communication for resource-constrained devices. Traditional HTTP, with its TCP overhead (connection establishment, persistent connections) and verbose text-based headers, is too heavy for microcontrollers with limited RAM, flash memory, and processing power. CoAP uses a compact binary header and runs over UDP. Its request/response model is similar to HTTP, making it familiar to developers, but its mechanisms are tailored for the IoT.

The URI scheme is coap:// or coaps:// (for DTLS security). Resources are identified by URIs, and requests are made using standard methods (GET, POST, PUT, DELETE) mapped to CoAP message codes. The message codes are compact integers: 0.01 for GET, 0.02 for POST, 0.03 for PUT, 0.04 for DELETE. Responses also have codes: 2.01 for CREATED, 2.04 for CHANGED, 2.05 for CONTENT.

The "observe" option (OBSERVE in the request header) is a key feature. When a client sends a GET request with the OBSERVE option set to 0, it’s asking the server to notify it whenever the resource’s state changes. The server then sends subsequent responses for that resource without the client needing to poll. This is crucial for event-driven IoT scenarios.

The most surprising thing about CoAP’s reliability is that it’s entirely built into the application layer on top of UDP. When a CoAP client sends a confirmable message (the default for GET, POST, PUT, DELETE), it expects an acknowledgment from the server within a certain timeout. If it doesn’t receive one, it retransmits the message. The server, upon receiving a confirmable message, must send back an acknowledgment. This ACK can carry a response payload if the server is ready, or it can be a separate message. This acknowledgment mechanism is what makes CoAP "reliable" over UDP, avoiding the full connection state and overhead of TCP.

The next hurdle is often integrating CoAP with existing web infrastructure, usually involving a CoAP-to-HTTP proxy.

Want structured learning?

Take the full Computer Networking course →