1227  CoAP Practice: Examples, Exercises, and Resources

1227.2 Worked Examples

NoteWorked Example: CoAP Payload Optimization for Battery-Powered Sensors

Scenario: A building deploys 200 battery-powered environmental sensors reporting temperature, humidity, CO2, and light levels every 5 minutes. Each sensor runs on a CR2032 coin cell (225 mAh capacity).

Given:

  • Current JSON payload: {"device_id":"ENV-042","temp":22.5,"humidity":45,"co2":412,"light":350,"ts":1704067200} = 82 bytes
  • CoAP header: 4 bytes (fixed) + 8 bytes (token + options) = 12 bytes
  • UDP/IP header: 28 bytes (8 UDP + 20 IP)
  • Radio transmission: 50 microamps per byte at 250 kbps
  • Radio startup energy: 15 mJ per transmission
  • Target battery life: 3 years

Steps:

  1. Calculate current energy consumption per message:
    • Total packet size: 82 (payload) + 12 (CoAP) + 28 (UDP/IP) = 122 bytes
    • Transmission energy: 122 Γ— 50 Β΅A Γ— (8 bits / 250 kbps) Γ— 3.0V = 0.59 mJ
    • Total per message: 15 mJ (startup) + 0.59 mJ (TX) = 15.59 mJ
    • Messages per day: 288 (every 5 minutes)
    • Daily energy: 288 Γ— 15.59 mJ = 4,490 mJ = 4.49 J
  2. Calculate battery life with current payload:
    • CR2032 capacity: 225 mAh Γ— 3.0V = 675 mWh = 2,430 J
    • Battery life: 2,430 J / 4.49 J/day = 541 days = 1.5 years (fails 3-year target)
  3. Optimize payload using CBOR encoding:
    • CBOR payload: {0:42,1:225,2:45,3:412,4:350,5:1704067200} = 23 bytes
    • Key mapping: 0=id, 1=tempΓ—10, 2=humidity, 3=co2, 4=light, 5=timestamp
    • New packet size: 23 + 12 + 28 = 63 bytes (48% reduction)
  4. Calculate optimized energy consumption:
    • Transmission energy: 63 Γ— 50 Β΅A Γ— 32 Β΅s Γ— 3.0V = 0.30 mJ
    • Total per message: 15 mJ + 0.30 mJ = 15.30 mJ
    • Daily energy: 288 Γ— 15.30 mJ = 4,406 mJ = 4.41 J
    • Battery life: 2,430 J / 4.41 J/day = 551 days (still fails)
  5. Further optimization - NON messages and batching:
    • Batch 3 readings per transmission (every 15 minutes)
    • NON message (no ACK wait) saves 200 ms radio-on time
    • Messages per day: 96 (every 15 minutes)
    • Batched CBOR payload: 3 Γ— 23 bytes = 69 bytes
    • Daily energy: 96 Γ— 15.30 mJ = 1,469 mJ = 1.47 J
    • Battery life: 2,430 J / 1.47 J/day = 1,653 days = 4.5 years (exceeds target)

Result: JSON to CBOR encoding reduces payload from 82 to 23 bytes (72% reduction). Combined with 15-minute batching, daily energy drops from 4.49 J to 1.47 J. Battery life extends from 1.5 years to 4.5 years, exceeding the 3-year target by 50%.

Key Insight: Radio startup energy (15 mJ) dominates per-byte transmission cost (0.005 mJ/byte). Batching 3 readings reduces daily transmissions from 288 to 96, saving 2.88 J/day - far more than the 0.08 J/day saved by payload compression alone. Always minimize transmission count before optimizing payload size.

NoteWorked Example: CoAP vs HTTP Energy Comparison for Smart Thermostat

Scenario: A smart thermostat needs to report current temperature and receive setpoint commands. Comparing CoAP over UDP vs HTTP over TCP for a device on Wi-Fi with 10% packet loss.

Given:

  • Read operation: GET current temperature (response: 8 bytes = β€œ22.5”)
  • Write operation: PUT new setpoint (request: 8 bytes = β€œ24.0”)
  • Wi-Fi radio: 150 mW active, 3.0V supply
  • Packet loss: 10% (requires retransmission)
  • Operations per hour: 60 reads + 2 writes

Steps:

  1. Calculate HTTP transaction overhead:
    • TCP handshake: 3 packets (SYN, SYN-ACK, ACK) = 180 bytes + 3 RTTs (150 ms total)
    • HTTP GET request: GET /temp HTTP/1.1\r\nHost: thermostat\r\n\r\n = 45 bytes
    • HTTP response: HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\n22.5 = 45 bytes
    • TCP teardown: 4 packets (FIN, ACK, FIN, ACK) = 240 bytes + 2 RTTs (100 ms)
    • Total per GET: 510 bytes, 250 ms radio time
    • Energy per GET: 150 mW Γ— 0.25 s = 37.5 mJ
  2. Calculate CoAP transaction overhead:
    • CoAP CON GET: 4 (header) + 4 (token) + 6 (URI option) = 14 bytes
    • CoAP ACK response: 4 (header) + 4 (token) + 4 (payload) = 12 bytes
    • UDP/IP overhead: 28 bytes Γ— 2 packets = 56 bytes
    • Total per GET: 82 bytes, 25 ms radio time (single RTT)
    • Energy per GET: 150 mW Γ— 0.025 s = 3.75 mJ
  3. Account for 10% packet loss retransmissions:
    • HTTP: TCP handles retransmission transparently, adds ~25 ms average (10% Γ— 250 ms)
    • Adjusted HTTP energy: 37.5 mJ Γ— 1.1 = 41.25 mJ per GET
    • CoAP CON: Retransmit after 2-second timeout on 10% of requests
    • Adjusted CoAP energy: (0.9 Γ— 3.75) + (0.1 Γ— 7.5) = 4.13 mJ per GET
  4. Calculate hourly energy consumption:
    • HTTP: 60 GETs Γ— 41.25 mJ + 2 PUTs Γ— 45 mJ = 2,565 mJ = 2.57 J/hour
    • CoAP: 60 GETs Γ— 4.13 mJ + 2 PUTs Γ— 5 mJ = 258 mJ = 0.26 J/hour
  5. Calculate daily and yearly energy:
    • HTTP: 2.57 J/hour Γ— 24 = 61.7 J/day = 22,520 J/year
    • CoAP: 0.26 J/hour Γ— 24 = 6.2 J/day = 2,263 J/year
    • Energy savings: 22,520 - 2,263 = 20,257 J/year (90% reduction)

Result: CoAP uses 0.26 J/hour vs HTTP’s 2.57 J/hour - a 10Γ— energy reduction. For a thermostat on USB power (unlimited energy), this difference is negligible. For a battery-powered variant with 10 Wh (36,000 J) battery, CoAP enables 16 years of operation vs HTTP’s 1.6 years.

Key Insight: HTTP’s TCP handshake (180 bytes, 150 ms) costs more than CoAP’s entire transaction (82 bytes, 25 ms). For request-response IoT patterns with small payloads, CoAP’s UDP-based approach provides 10Γ— energy efficiency. However, CoAP requires application-level retry logic for reliability, while HTTP/TCP handles retransmission automatically - choose based on whether developer simplicity or energy efficiency is the priority.

1227.3 Summary

This chapter covered the Constrained Application Protocol (CoAP) for resource-constrained IoT devices:

  • Lightweight Design: 4-byte minimum header overhead (25Γ— smaller than HTTP) enables efficient communication on constrained networks with limited bandwidth and battery power
  • UDP-Based Transport: Built on UDP rather than TCP, eliminating connection setup overhead and reducing latency, ideal for battery-powered sensors that need quick request-response cycles
  • RESTful Architecture: GET, POST, PUT, DELETE methods with URI-based resource addressing familiar to HTTP developers, making CoAP easy to learn and integrate with web services
  • Confirmable vs Non-Confirmable: CON messages require acknowledgment for reliability (critical commands), while NON messages provide fire-and-forget efficiency for frequent sensor readings
  • Observe Extension: Publish-subscribe pattern where clients observe resources and servers push updates when values change, eliminating constant polling and saving battery
  • Block-wise Transfers: Large payloads fragmented into manageable blocks for transmission over constrained networks with limited MTU sizes
  • Multicast Support: IPv6 multicast address (FF0X::FD) enables one-to-many communication for device discovery and group commands without requiring individual requests

Protocol Comparisons: - MQTT - Publish-subscribe alternative to CoAP - Application Protocols Overview - Compare all IoT protocols - CoAP Fundamentals & Architecture - Deep dive into CoAP design

Architecture & Implementation: - Edge Fog Computing - CoAP in edge architectures - IoT Reference Models - Protocol layer placement

Security: - DTLS and Security - Securing CoAP with DTLS - Security Overview - IoT security principles

Hands-On: - CoAP Features & Labs - Practical CoAP implementations

Learning Hubs: - Simulations Hub - Interactive tools - Quiz Navigator - Test your understanding

1227.4 Practice Exercises

Build practical skills with these hands-on CoAP exercises, progressing from resource discovery to production-grade implementations.

Objective: Implement CoAP resource discovery using .well-known/core endpoint.

Tasks: 1. Set up a CoAP server with multiple resources: - /sensors/temperature (readable) - /sensors/humidity (readable) - /actuators/led (writable) - /config/network (readable and writable) 2. Implement .well-known/core endpoint that returns resources in CoRE Link Format 3. Create CoAP client that: - Performs resource discovery: GET coap://server/.well-known/core - Parses Link Format response - Displays available resources with their attributes (content-type, methods) 4. Test resource filtering: GET coap://server/.well-known/core?rt=temperature

Expected Outcome: - Server responds with all resources in CoRE Link Format - Client successfully discovers and lists all available endpoints - Resource types (rt), interfaces (if), and content formats visible - Filtering by resource type returns subset of resources

Hints: - CoRE Link Format: </path>;rt="resource-type";if="interface";ct=0 - Use semicolon to separate attributes, comma to separate resources - Implement resource filtering on server side based on query parameters - Test with: coap-client -m get coap://localhost/.well-known/core

Server Response Example:

</sensors/temperature>;rt="temperature-c";ct=0,
</sensors/humidity>;rt="humidity-p";ct=0,
</actuators/led>;rt="light-ctl";ct=0;if="actuator",
</config/network>;rt="config";ct=50;if="config"

Python Server Implementation:

import aiocoap
import aiocoap.resource as resource

class WellKnownCore(resource.Resource):
    async def render_get(self, request):
        # Generate CoRE Link Format response
        links = [
            '</sensors/temperature>;rt="temperature-c";ct=0',
            '</sensors/humidity>;rt="humidity-p";ct=0',
            '</actuators/led>;rt="light-ctl";if="actuator"',
            '</config/network>;rt="config";ct=50;if="config"'
        ]
        payload = ','.join(links).encode('utf-8')

        return aiocoap.Message(
            payload=payload,
            content_format=40  # application/link-format
        )

# Register resource
root = resource.Site()
root.add_resource(['.well-known', 'core'], WellKnownCore())

Objective: Build hardware actuator control system using CoAP PUT requests.

Tasks: 1. Set up ESP32 with: - LED connected to GPIO pin - Wi-Fi connectivity - CoAP server library (coap-simple or MicroCoAP) 2. Implement CoAP resources: - GET /led/status - Returns β€œon” or β€œoff” - PUT /led/state - Accepts β€œ1” (on) or β€œ0” (off) to control LED - GET /system/uptime - Returns device uptime in seconds 3. Create Python CoAP client that: - Discovers ESP32 resources via .well-known/core - Toggles LED every 5 seconds using PUT requests - Verifies state changes with GET requests - Logs uptime periodically 4. Test with both CON and NON message types, measure latency

Expected Outcome: - ESP32 responds to PUT requests by toggling LED - GET requests return current LED state and uptime - Observe latency difference: CON (~50-100ms), NON (~20-50ms) - System runs reliably without memory leaks

Hints: - Use client.setCallback() to handle incoming PUT requests on ESP32 - Return 2.04 Changed response code after successful LED toggle - Add Last Will Testament to detect ESP32 disconnection - Test reliability by disconnecting ESP32 Wi-Fi temporarily

ESP32 CoAP Server Code:

#include <Wi-Fi.h>
#include <coap-simple.h>

const char* ssid = "your-wifi";
const char* password = "your-password";

Wi-FiUDP udp;
Coap coap(udp);

int ledPin = 2;
bool ledState = false;

// Callback for PUT /led/state
void callback_led_put(CoapPacket &packet, IPAddress ip, int port) {
    if (packet.payloadlen > 0) {
        ledState = (packet.payload[0] == '1');
        digitalWrite(ledPin, ledState ? HIGH : LOW);

        char response[4];
        sprintf(response, "%d", ledState);

        coap.sendResponse(ip, port, packet.messageid,
                          response, strlen(response),
                          COAP_CHANGED, COAP_TEXT_PLAIN,
                          packet.token, packet.tokenlen);
    }
}

// Callback for GET /led/status
void callback_led_get(CoapPacket &packet, IPAddress ip, int port) {
    char response[4];
    sprintf(response, "%s", ledState ? "on" : "off");

    coap.sendResponse(ip, port, packet.messageid,
                      response, strlen(response),
                      COAP_CONTENT, COAP_TEXT_PLAIN,
                      packet.token, packet.tokenlen);
}

void setup() {
    pinMode(ledPin, OUTPUT);
    Wi-Fi.begin(ssid, password);

    while (Wi-Fi.status() != WL_CONNECTED) {
        delay(500);
    }

    // Register CoAP resources
    coap.server(callback_led_get, "led/status");
    coap.server(callback_led_put, "led/state");
    coap.start();
}

void loop() {
    coap.loop();
    delay(10);
}

Python Client:

import asyncio
import aiocoap

async def toggle_led():
    protocol = await aiocoap.Context.create_client_context()

    for i in range(10):
        # Toggle LED on
        request = aiocoap.Message(code=aiocoap.PUT,
                                  uri='coap://esp32.local/led/state',
                                  payload=b'1')
        await protocol.request(request).response
        print("LED ON")

        await asyncio.sleep(2)

        # Toggle LED off
        request = aiocoap.Message(code=aiocoap.PUT,
                                  uri='coap://esp32.local/led/state',
                                  payload=b'0')
        await protocol.request(request).response
        print("LED OFF")

        await asyncio.sleep(2)

asyncio.run(toggle_led())

Objective: Implement multicast group communication for automatic device discovery.

Tasks: 1. Create multiple CoAP servers (3-5 instances) simulating different sensors: - Temperature sensor on port 5683 - Humidity sensor on port 5684 - Pressure sensor on port 5685 2. Configure each server to: - Listen on IPv6 multicast address FF02::FD (link-local all-CoAP-nodes) - Respond to multicast GET requests - Provide unique device ID and sensor type in response 3. Create multicast client that: - Sends NON GET request to coap://[FF02::FD]/sensors - Collects responses from all reachable sensors (wait 2 seconds) - Displays discovered devices with their capabilities 4. Test with devices on same network segment

Expected Outcome: - Single multicast request discovers all sensors simultaneously - Each sensor responds with its device ID and sensor type - Client receives multiple responses within 2-second window - Understand multicast efficiency vs. individual unicast requests

Hints: - Use NON messages for multicast (CON not supported for multicast) - Set multicast TTL (hop limit) appropriately: link-local (255) vs. site-local - Handle duplicate responses (some sensors may respond multiple times) - Test with: coap-client -m get -N coap://[FF02::FD]/sensors

Python Multicast Server:

import asyncio
import aiocoap
import aiocoap.resource as resource
import socket

class SensorResource(resource.Resource):
    def __init__(self, device_id, sensor_type):
        super().__init__()
        self.device_id = device_id
        self.sensor_type = sensor_type

    async def render_get(self, request):
        payload = f"Device: {self.device_id}, Type: {self.sensor_type}".encode()
        return aiocoap.Message(payload=payload)

async def main():
    root = resource.Site()
    root.add_resource(['sensors'], SensorResource("temp-sensor-01", "temperature"))

    # Create server context with multicast support
    context = await aiocoap.Context.create_server_context(root, bind=('::', 5683))

    # Join IPv6 multicast group
    sock = context.serversite.transport._sock
    mreq = socket.inet_pton(socket.AF_INET6, 'ff02::fd') + b'\x00' * 4
    sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)

    await asyncio.get_running_loop().create_future()

asyncio.run(main())

Multicast Client:

import asyncio
import aiocoap

async def discover_sensors():
    protocol = await aiocoap.Context.create_client_context()

    request = aiocoap.Message(
        code=aiocoap.GET,
        uri='coap://[ff02::fd]/sensors',
        mtype=aiocoap.NON  # Multicast must use NON
    )

    print("Sending multicast discovery...")
    discovered = []

    try:
        response = await asyncio.wait_for(
            protocol.request(request).response,
            timeout=2.0
        )
        discovered.append(response.payload.decode())
    except asyncio.TimeoutError:
        pass

    print(f"Discovered {len(discovered)} devices:")
    for device in discovered:
        print(f"  - {device}")

asyncio.run(discover_sensors())

Objective: Understand how CoAP devices can be integrated with HTTP-based web services through protocol translation.

Concept: A CoAP-HTTP proxy acts as a bridge, allowing HTTP clients (web browsers, mobile apps) to access CoAP devices without implementing CoAP themselves.

How It Works:

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#E67E22', 'secondaryColor': '#16A085', 'tertiaryColor': '#E67E22'}}}%%
sequenceDiagram
    participant Browser as Web Browser<br/>(HTTP Client)
    participant Proxy as CoAP-HTTP<br/>Proxy Gateway
    participant Sensor as CoAP Sensor<br/>(Battery-powered)

    Browser->>Proxy: HTTP GET /coap/sensor1/temperature
    Note over Proxy: Translate to CoAP
    Proxy->>Sensor: CoAP GET /temperature

    Sensor->>Sensor: Read sensor
    Sensor->>Proxy: CoAP 2.05 Content<br/>Payload: "23.5Β°C"<br/>Max-Age: 60s

    Note over Proxy: Cache response<br/>for 60 seconds
    Proxy->>Browser: HTTP 200 OK<br/>{"temp": "23.5", "cached": false}

    Note over Browser,Sensor: Second request within 60s cache window

    Browser->>Proxy: HTTP GET /coap/sensor1/temperature
    Note over Proxy: Serve from cache<br/>No CoAP request
    Proxy->>Browser: HTTP 200 OK<br/>{"temp": "23.5", "cached": true}

    rect rgb(230, 126, 34)
        Note over Proxy,Sensor: Battery savings: 50-80% reduction<br/>in CoAP requests via caching
    end

Figure 1227.4: CoAP-HTTP Proxy Gateway with Caching for Web Integration

Key Concepts:

1. Protocol Translation: - HTTP GET β†’ CoAP GET - HTTP POST β†’ CoAP POST - HTTP PUT β†’ CoAP PUT - HTTP DELETE β†’ CoAP DELETE - HTTP 200 OK ← CoAP 2.05 Content - HTTP 404 Not Found ← CoAP 4.04 Not Found

2. Caching Strategy: - Proxy respects CoAP Max-Age option - Reduces load on battery-powered sensors - Typical cache duration: 30-120 seconds - Dramatically extends battery life

3. URI Mapping:

HTTP:  http://proxy.example.com/coap/device123/temperature
         ↓
CoAP:  coap://device123.local/temperature

Performance Benefits:

Metric Direct CoAP HTTP Proxy (no cache) HTTP Proxy (cached)
Latency 20-50ms 30-70ms 1-5ms
CoAP requests 100 100 20 (80% reduction)
Battery impact High High Low
Web compatibility No Yes Yes

Implementation Approaches:

  1. HTTP Server Options: Flask, FastAPI, Node.js Express
  2. CoAP Client Libraries: aiocoap (Python), node-coap (Node.js)
  3. Caching: Redis, in-memory dictionaries with TTL
  4. Production Proxies: Californium cf-proxy, Eclipse Leshan

Practical Applications: - Web dashboards accessing CoAP sensors - Mobile apps controlling CoAP smart home devices - Cloud services aggregating CoAP data - Legacy HTTP systems integrating with modern CoAP networks

Testing Tools:

# Test CoAP directly
coap-client -m get coap://sensor1.local/temperature

# Test via HTTP proxy
curl http://localhost:8000/coap/sensor1/temperature

Trade-offs: - βœ… Web compatibility (use standard HTTP tools) - βœ… Battery savings through caching - βœ… Simplified client development - ⚠️ Added latency (proxy hop) - ⚠️ Single point of failure - ⚠️ Requires proxy infrastructure

Deep Dives: - CoAP Architecture - Protocol design and technical details - CoAP Features and Labs - Hands-on implementations and testing

Protocol Comparisons: - MQTT Fundamentals - Publish-subscribe alternative to CoAP - AMQP vs MQTT - Application protocol selection guide - IoT Protocols Review - Comprehensive comparison of all protocols

Architecture: - Edge Computing - Where CoAP runs in IoT architectures - IoT Reference Models - Protocol layer placement

Security: - Security Overview - Securing CoAP with DTLS

Learning Resources: - Simulations Hub - Interactive CoAP protocol demonstrations - Quiz Navigator - Test your understanding

The following figure from the NPTEL Internet of Things course provides additional perspective on IoT architectures relevant to CoAP implementations.

IoT Sensor Node Architecture:

Sensor node architecture diagram showing four main components: Sensor and Actuator Unit for data collection and physical interaction, Processing and Memory Unit as central controller with storage, Wireless Communication Unit for network connectivity, and Power Management Unit coordinating energy supply to all components

Sensor node architecture showing Processing, Communication, Sensing, and Power Management units

This sensor node architecture illustrates the constrained devices that CoAP is designed for - devices with limited processing, memory, and power resources where CoAP’s lightweight design provides significant advantages over HTTP.

Related Protocol Stacks:

For IEEE 802.15.4 protocol stack and OSI mapping, see the 6LoWPAN Fundamentals chapter. CoAP runs at the Application layer, using UDP/IP over 6LoWPAN adaptation, which in turn uses IEEE 802.15.4 for the lower layers.

Source: NPTEL Internet of Things Course, IIT Kharagpur

1227.5 Further Reading

1227.6 What’s Next

Now that you understand CoAP, explore the other IoT messaging and networking protocols:

  • Next Chapter: AMQP - Understand advanced message queuing for enterprise IoT
  • Then: XMPP - Explore extensible messaging and presence protocols
  • Data Management: Continue to Chapter 5 for data management and analytics
  • Wireless Networks: For comparison, return to LoRaWAN to understand long-range, low-power wireless networks