Chapters

23 CoAP Lab: Message Construction

coap
implementation

23.1 Start With the Decision

This section: Start With the Decision — CoAP is a compact request and response protocol.

CoAP is a compact request and response protocol. A packet is one unit sent across a network. In a few bytes, a CoAP packet holds a type that says how to handle delivery. A token links a reply to its request; a code names the request or result; and an ID identifies the message. Build each field before sending the packet.

23.2 Route Overview

This section: Route Overview — This is part 2 of 2.

This is part 2 of 2. Review CoAP Lab: Stack and Trace Contracts for the preceding evidence.

23.3 Learning Objectives

This section: Learning Objectives

  • Encode CoAP header fields and token length into message bytes.
  • Match tokens and message IDs across replies and retries.

23.4 Chapter Roadmap

This section: Chapter Roadmap

  • Try It: CoAP Message Byte Builder
  • Checkpoint: ESP32 Packet Evidence
  • Response Code Reference
  • Knowledge Check: CoAP Response Codes
  • Basic Simulator: CoAP UDP Communication
  • Interactive Simulator: CoAP-Style UDP Communication
  • Learning Points
  • Visual Reference: CoAP Message Structure
  • Visual: CoAP Message Structure
  • Alternative View: CoAP vs HTTP Header Comparison
  • Common Mistake: Misunderstanding CoAP Token vs Message ID Correlation
  • Try It: Token vs Message ID Correlation Visualizer
  • Checkpoint: Correlation Boundary
  • Common Pitfalls
  • 1. Using Confirmable Messages for Every CoAP Request
  • 2. Ignoring CoAP Proxy Caching Semantics
  • 3. Forgetting DTLS Session Management
  • Label the Diagram
  • Order the Steps
  • Concept Relationships
  • See Also
  • Match the Concepts
  • What’s Next
  • Advanced Lab Consolidation Notes
  • Checkpoint: Lab Readiness Evidence
  • Summary
  • Key Takeaway
Try It: CoAP Message Byte Builder

Construct a CoAP message byte-by-byte and see exactly how the 4-byte header is encoded. This helps you understand what the coap-simple library constructs behind the scenes when you call coap.get() or coap.put().

23.4.1 Step 1: Encode and inspect a real localhost UDP datagram

  • In a Python terminal, send a CON GET for /temperature over UDP to a local CoAP responder; inspect the first four bytes before any option or payload.
  • The real local request was 42 01 12 34 aa bb bb 74 65 6d 70 65 72 61 74 75 72 65: version 1, CON, token length 2, GET, and message ID 0x1234.
  • The response bytes were 62 45 12 34 aa bb ff 32 32 2e 35: ACK, 2.05 Content, the same message ID and token, then payload 22.5.
  • The local decoder printed Version=1 Type=CON TKL=2 Code=0.01 MID=0x1234 Token=aa bb URI-Path=/temperature and Response Type=ACK Code=2.05 MID=0x1234 Token=aa bb Payload=22.5 from those exchanged bytes.
  • In the byte builder below, set version 1, CON, TKL 2, Code 0.01, and MID 4660 to compare the four-byte header 42 01 12 34.
  • The byte builder previews header fields only; the local UDP exchange is the packet evidence for the token, URI option, and response payload.

This is a header-field exercise. Some Type and Code combinations in the controls are not valid complete CoAP exchanges; the preview does not generate Token, option, or payload bytes.

This section:

Broker Bex, Messaging and broker domain expertCheckpoint: ESP32 Packet Evidence
  • You now know where the ESP32 callback extracts payload bytes and decodes the response-code class and detail.
  • You can connect coap.get(), coap.put(), and coap.post() calls to resource paths already tested in Python.
  • You have a byte-level header view that explains what the library sends before the payload leaves the device.

Once packets can be decoded, response codes become the lab’s acceptance language. Use the table below to make server behavior explicit.

23.5 Response Code Reference

This section: Response Code Reference

CoAP response codes follow a Class.Detail format similar to HTTP:

CodeMeaningHTTP Equivalent
2.01Created201 Created
2.02Deleted200 OK (for DELETE)
2.03Valid304 Not Modified
2.04Changed200 OK (for PUT)
2.05Content200 OK (for GET)
4.00Bad Request400 Bad Request
4.01Unauthorized401 Unauthorized
4.04Not Found404 Not Found
4.05Method Not Allowed405 Method Not Allowed
5.00Internal Server Error500 Internal Server Error
# Checking response codes in Python
if response.code.is_successful():
    print("Success!")
elif response.code == Code.NOT_FOUND:
    print("Resource not found")
elif response.code == Code.BAD_REQUEST:
    print("Invalid request format")
Knowledge Check: CoAP Response Codes

23.6 Basic Simulator: CoAP UDP Communication

This section: Basic Simulator: CoAP UDP Communication

23.6.1 Step 2: Step through the browser message exchange

  • Open the CoAP Message Types Workbench below in the browser and select Separate response.
  • Press Step once; the run here displayed Separate response and Step 2 of 4 in the active scenario readout.
  • In Current Packet Format, compare the empty ACK’s message ID with the original CON before advancing to the later response.
  • Continue to the later CON response and compare its token with the original request; token correlation survives the changed message ID.
  • This browser simulation teaches the packet sequence; the localhost UDP bytes in Step 1 provide an independently executed packet exchange.
Interactive Simulator: CoAP-Style UDP Communication

What This Simulates: ESP32 demonstrating CoAP’s lightweight UDP request/response pattern

Run it: Before you open the Wokwi build, drive the same request/response in the browser workbench below. Step through a Piggybacked CON GET, then a Separate response and a NON request/response, and watch the Current Packet Format and Event Trace show how CON, NON, ACK, and RST differ on the wire. Try Duplicate CON handling and a Reset message to expose the edge cases the serial monitor alone hides, and adjust Token length, Payload bytes, and Uri-Path segments to feel the 4-byte-header economy. The Wokwi steps below then reproduce the same exchange on the ESP32.

CoAP Communication Pattern:

Client (ESP32)
    |
    | GET /temperature
    | 4-byte header over UDP 5683
    v
Server (Simulated)
    |
    | 2.05 Content
    | Temp: 23.5C
    v
Client receives response

23.6.2 Step 3: Inspect the ESP32 serial demonstration if Wokwi is available

  • Open the linked Wokwi project below in a browser and click Start Simulation if the service has capacity.
  • In Serial Monitor, find the request path, response code, message type labels, and reported round-trip time.
  • Compare those printed fields with the real localhost UDP bytes in Step 1 and the browser workbench sequence in Step 2.
  • Preserve the project’s serial transcript, but do not treat a printed CoAP-style line as a Wireshark packet capture unless the sketch actually transmits UDP.
  • Wokwi was not run in this lane because the CI quota is exhausted; no ESP32 serial or radio result is claimed here.
Learning Points

What You’ll Observe:

  1. UDP Transport - Connectionless, lightweight communication
  2. 4-Byte Headers - Minimal overhead compared to HTTP
  3. Request/Response - RESTful pattern like HTTP GET
  4. Message IDs - Matching ACKs and detecting duplicates
  5. Tokens - Correlating responses to requests
  6. No Handshake - Direct communication without TCP overhead

23.7 Visual Reference: CoAP Message Structure

This section: Visual Reference: CoAP Message Structure — The remaining checks turn those protocol facts into operational judgment before you reuse this lab pattern in a production prototype.

Visual: CoAP Message Structure

Before decoding a capture, inspect the layout in Figure 23.1 to locate the fixed boundary and the fields whose lengths are carried inside the message. This prevents options or payload bytes from being mistaken for header fields.

CoAP message format diagram showing the fixed header bit fields (Ver, Type, TKL, Code, Message ID), token, options, payload marker, and payload.
Figure 23.1: CoAP messages combine a fixed header with an optional token, options, payload marker, and payload.

In Figure 23.1, the first four bytes always provide version, type, token length, code, and Message ID. TKL then tells the decoder how many token bytes follow; delta-encoded options continue until the optional 0xFF payload marker, after which the remaining bytes are payload. That order is the lab’s packet-decoding checklist.

The 4-byte fixed header contains:

  • Ver: Version (always 1)
  • Type: CON(0), NON(1), ACK(2), RST(3)
  • TKL: Base-format Token length (0-8 bytes); RFC 8974 defines optional extended tokens
  • Code: Method (GET=1, POST=2, PUT=3, DELETE=4) or response code
  • Message ID: 16-bit identifier for ACK matching and duplicate detection
Alternative View: CoAP vs HTTP Header Comparison

The compact field layout matters only when compared on the wire. Inspect Figure 23.2 to distinguish CoAP’s binary application header from the transport setup and textual headers in the HTTP example.

Side-by-side comparison of CoAP and HTTP protocol efficiency: HTTP/1.1 textual request and response metadata plus TCP connection setup versus CoAP’s compact binary header and datagram transport; the diagram compares framing and setup rather than byte totals.
Figure 23.2: CoAP reduces header and transport overhead relative to HTTP/1.1 for constrained request-response exchanges.

In Figure 23.2, inspect the HTTP side first: request and response metadata are textual, and TCP plus TLS add connection and security exchanges. Then compare the CoAP side’s four-byte fixed header and optional fields over UDP. The lesson is not that every complete CoAP packet is four bytes; it is that CoAP keeps the fixed application framing small and pays only for fields used by that exchange.

Common Mistake: Misunderstanding CoAP Token vs Message ID Correlation

The Error: Developers new to CoAP often confuse Message ID (MID) and Token, attempting to match responses to requests using MID alone. This causes failures with Observe notifications and separate responses.

Why It Happens: HTTP developers expect a single request-response correlation mechanism. CoAP has two: MID for duplicate detection and Token for logical request-response matching.

Example of Failure:

# WRONG: Matching by Message ID only
request_mid = 12345
response = await get_response()
if response.message_id == request_mid:  # Fails with Observe!
    process(response)

The Fix: Always use Token for request-response correlation:

# CORRECT: Matching by Token
import secrets

# Client sends request with unique token
request_token = secrets.token_bytes(4)  # e.g., 0xAB12CD34
request = Message(code=Code.GET, uri='coap://sensor/temp')
request.token = request_token

# For Observe, server sends multiple responses with DIFFERENT MIDs
# but SAME token as original request
async for response in request_handle.observation:
    if response.token == request_token:  # Correct correlation
        print(f'Update: {response.payload}')

Why This Matters:

  1. Separate Responses: Server sends empty ACK (MID=12345) immediately, then data CON (MID=12346) later. Both share the same Token.
  2. Observe Notifications: Each notification has a new MID (12347, 12348, 12349…) but the original Token throughout the subscription lifetime.
  3. Concurrent Requests: Client sends multiple requests simultaneously. Responses may arrive out-of-order. Token uniquely identifies which request each response answers.

Illustrative failure: A client that discards Observe notifications with new Message IDs can lose readings. Correlate notifications by the observation Token; use Message ID for ACK and duplicate handling.

Try It: Token vs Message ID Correlation Visualizer

See why Token-based matching works but MID-based matching fails. This simulator shows concurrent requests and Observe notifications where MIDs change but Tokens remain constant.

23.7.1 Step 4: Test the correlation key

  • In the visualizer below, choose two concurrent requests and three Observe notifications, then compare Token matching with MID matching.
  • A reply must carry the request’s token to identify the application exchange, while an ACK uses the CON’s message ID to acknowledge that particular transmission.
  • In the local UDP run, request and reply both used token aa bb and message ID 0x1234; that case alone cannot demonstrate a later separate response with a new MID.
  • Use the Separate response browser scenario from Step 2 to see the changed MID while the token still identifies the request.
  • Record each request token, each response token, and the message IDs separately; matching only by MID would misassociate a later notification or separate response.
Broker Bex, Messaging and broker domain expertCheckpoint: Correlation Boundary
  • You now know the boundary between MID duplicate detection and Token request-response correlation.
  • You can explain why Observe notifications keep the original Token even when each notification has a new MID.
  • You have a concrete failure pattern to look for when a client drops valid updates from a working server.

The remaining checks turn those protocol facts into operational judgment before you reuse this lab pattern in a production prototype.

Common Pitfalls

This section: Common Pitfalls

CON messages require an ACK exchange. With RFC 7252 defaults, four retransmissions occur after randomized waits totaling 30–45 seconds; the final ACK wait can extend failure detection to 62–93 seconds. For example, even at an illustrative 20% packet-loss rate, loss alone does not determine a fixed delay. Use NON for periodic telemetry where data freshness matters more than guaranteed delivery; reserve CON for actuation commands.

CoAP proxies cache GET responses based on Max-Age option — a sensor returning temperature with Max-Age=60 will serve cached values for 60 seconds even if the physical reading changes. Set Max-Age to match your data freshness requirement, not the default 60 seconds.

A fresh DTLS handshake can dominate a short CoAP exchange; its round trips and latency depend on DTLS version, authentication, loss, and link delay. Reuse an established session where appropriate, or use the resumption mechanism supported by the deployed DTLS version (RFC 9147 for DTLS 1.3).

This section:

This section:

This section:

This section:

23.8 Concept Relationships

This section: Concept Relationships — Start with CoAP fundamentals for the request model, then use the message format to inspect response codes and fields.

This implementation chapter bridges theory to practice:

Foundation Knowledge:

Start with CoAP fundamentals for the request model, then use the message format to inspect response codes and fields.

Programming Concepts:

Read the Python guide first. Then compare the Arduino examples.

Implementation Details:

  • Token matching for request-response correlation
  • Resource handler registration (render_get, render_put)
  • Observe pattern (observe=0 option, notification loop)
  • Response code selection (2.01 Created, 2.04 Changed, 2.05 Content)

Next Steps:

Use the advanced lab to exercise Observe and block transfer, then apply the API design guidance to the service interface.

23.9 See Also

This section: See Also — Start with the aiocoap library guide, then review Python async programming for the event loop used by the examples.

Python CoAP Resources:

Start with the aiocoap library guide, then review Python async programming for the event loop used by the examples.

ESP32 CoAP Resources:

Read the ESP32 framework guide before the CoAP library guide to see where the library runs.

Testing and Debugging:

Use Wireshark to inspect CoAP traffic, then use the client tool to send requests you can trace.

Alternative Implementations:

Compare the C and JavaScript CoAP libraries when choosing an implementation for the target runtime.

23.10 What’s Next

This section: What’s Next

CoAP Implementation Stack and Trace Contracts

Focus: Stack selection, handler mapping, trace evidence, Token correlation, CON/NON policy, and DTLS session reuse.

Why read it: Convert the lab code into an acceptance contract you can verify with coap-client, Wireshark, and server logs.

CoAP Advanced Features Lab

Focus: Block-wise transfer, Observe with ESP32, and resource discovery via /.well-known/core.

Why read it: Build and run a full Wokwi simulation that extends the patterns from this chapter.

CoAP Methods and Patterns

Focus: Design decisions for GET, POST, PUT, DELETE, and CON vs NON tradeoffs.

Why read it: Deepen your understanding of when and why each method and message type is chosen.

CoAP Fundamentals and Architecture

Focus: Message structure, token/MID fields, and the UDP transport layer.

Why read it: Revisit the protocol internals now that you have seen them in working code.

CoAP API Design

Focus: REST resource modelling, URI design, and Content-Format selection.

Why read it: Apply production best practices to the servers and clients you have built here.

DTLS and Security

Focus: Datagram TLS for CoAP, OSCORE, and pre-shared keys.

Why read it: Secure the CoAP servers you implemented in this chapter for real deployments.

MQTT Implementation Labs

Focus: Broker-based publish/subscribe with Python and ESP32.

Why read it: Compare broker-mediated MQTT patterns against CoAP's direct request/response and Observe.

23.11 Advanced Lab Consolidation Notes

This section: Advanced Lab Consolidation Notes — For battery labs, write the message policy beside the resource table.

When extending the basic labs into a fuller ESP32-style CoAP server, keep the feature set small enough to test:

Resource or featureLab behavior to proveFailure mode to observe
/temperature with ObserveClient registers once, then receives updates only when the reading changesLost notification, cancelled observation, or stale token
/firmware with Block2Client can fetch a large payload in numbered blocks and resume from the failed blockRestarting the whole transfer after one missing block
/.well-known/coreDiscovery lists only supported resources and content formatsClient assumes hidden or unsupported paths
Configuration PUTIdempotent update can be retried without creating duplicate stateRetried POST creates repeated configuration records
CON/NON selectionCommands and rare critical alerts use CON; periodic telemetry can use NON when the next value replaces the previous oneHigh-frequency CON drains batteries, while NON commands lose safety-critical acknowledgements

For battery labs, write the message policy beside the resource table. A common hybrid is NON for routine telemetry and CON for user actions, safety alerts, firmware blocks, or configuration changes. That preserves the energy benefit of UDP while still proving delivery for messages that cannot be silently lost.

23.11.1 Block Transfer Planning Check

Before using Block2 for firmware, images, or larger JSON payloads, record the block size, total block count, resume behavior, and retry limit. A small planning record prevents the lab from hiding a production risk:

23.11.1.1 Step 5: Record a block transfer and loss response

  • In the CoAP Block-Wise Transfer Workbench below, select Lossy field link and press Step.
  • The browser run here showed Block2 2:0/1/256 (value 12) and byte range 0-255 for the first 256-byte block.
  • Continue stepping through the loss until the event trace shows which NUM is retried, then check that previously received blocks stay complete.
  • Fill the planning table with payload size, negotiated block size, count, retry limit, token, and ETag or version before approving resume.
  • This is a browser protocol model; firmware or server acceptance still needs a real constrained-link trace with block numbers and a final checksum.

Run it: Fill this planning record from the block-wise workbench below instead of guessing. Pick a direction (Block2 download or Block1 upload) and a Scenario such as Firmware upload or Sensor log download, choose a Requested block size (SZX), and step the transfer while watching NUM, the M bit, and the Byte range advance — that tells you how many blocks a payload needs. Then run the Lossy field link and Server asks for smaller blocks scenarios and read the Event trace to confirm the client retries only the missing block rather than restarting, which is exactly the resume behavior this check must capture.

QuestionEvidence to capture
How many blocks are needed?Payload bytes divided by negotiated block size, with the final partial block noted
What happens after packet loss?Client retries only the missing block instead of restarting the whole payload
How is progress verified?Server logs block number, token, ETag or version, and final checksum
When is Block2 not enough?If payloads are frequent or very large, move the transfer to a gateway or HTTP/TLS path and keep CoAP for control

This section:

Broker Bex, Messaging and broker domain expertCheckpoint: Lab Readiness Evidence
  • You now know which records to capture for resource discovery, configuration updates, Observe cancellation, Block2 resume behavior, and security setup.
  • You can choose NON for replaceable telemetry and CON for commands, alerts, firmware blocks, and configuration changes.
  • You have a final checklist for deciding whether the implementation is tested under constrained-network conditions rather than only compiled.

23.12 Summary

This section: Summary — CoAP implementation labs turn protocol rules into testable behavior: resource handlers, confirmable messages, retransmission, Observe notifications, and security setup.

CoAP implementation labs turn protocol rules into testable behavior: resource handlers, confirmable messages, retransmission, Observe notifications, and security setup. The goal is to verify behavior with real requests rather than assuming the stack handles every edge case.

23.13 Key Takeaway

This section: Key Takeaway — A CoAP implementation is ready only when resource paths, content formats, timeout handling, retransmission, Observe cancellation, and security settings have all been exercised under constrained-network conditions.

A CoAP implementation is ready only when resource paths, content formats, timeout handling, retransmission, Observe cancellation, and security settings have all been exercised under constrained-network conditions.

23.14 Continue Your Route

This section: Continue Your Route — This is the last part of this chapter.

This is the last part of this chapter. Return to CoAP Lab: Stack and Trace Contracts or continue from the coap module index.

Find help

3 reviewed answers.

Where should I start in a module?

Open the module page from the sidebar to see its chapters in order. Use the module map to return to a topic you need.

Modules and paths

Reviewed

What can I try when a project does not work?

Check the symptom, then work through the diagnostic steps. Keep the error message and the smallest example that reproduces it.

When you are stuck

Reviewed

How do interactive examples work?

Use the interactive guide to check how examples and simulations work before changing your project.

Interactive examples guide

Reviewed