42  CoAP Message Format and Structure

In 60 Seconds

A CoAP message consists of a compact 4-byte fixed header (version, type, token length, code, message ID), an optional token for request-response matching, delta-compressed options, and an optional payload. Understanding this binary format is essential for debugging network captures and optimizing message sizes on constrained networks where every byte impacts battery life and airtime cost.

42.1 Learning Objectives

By the end of this chapter, you will be able to:

  • Decode CoAP Headers: Analyze and decode the 4-byte fixed header field-by-field, identifying version, type, token length, code, and message ID
  • Distinguish Options Encoding: Explain how delta compression works and calculate the byte savings compared to naive full-number encoding
  • Apply Response Codes: Select correct 2.xx/4.xx/5.xx codes for specific scenarios and justify the choice
  • Implement Token Matching: Construct proper request-response correlation logic using tokens rather than message IDs
  • Compare CoAP and HTTP: Calculate message size differences and evaluate the energy impact on battery-powered constrained devices
  • Diagnose Message Format Errors: Identify common parsing mistakes such as confusing Message ID with Token and incorrect Content-Format selection
  • CoAP: Constrained Application Protocol — REST-style request/response protocol using UDP instead of TCP
  • Confirmable Message (CON): Requires ACK from recipient — provides reliable delivery over UDP at the cost of one roundtrip
  • Non-confirmable Message (NON): Fire-and-forget UDP datagram — lowest latency, no delivery guarantee
  • Observe Option: CoAP extension enabling publish/subscribe: client registers to receive notifications on resource changes
  • Block-wise Transfer: Fragmentation mechanism for transferring payloads larger than a single CoAP datagram
  • Token: Client-generated value matching responses to requests — enables concurrent request/response pairing
  • DTLS: Datagram TLS — CoAP’s security layer providing encryption and authentication over UDP

42.2 For Beginners: CoAP Message Format

Every CoAP message has a specific structure – a compact header followed by optional fields and the actual data. Think of it like a postcard: the header is the address and stamp (small, fixed size), and the rest is your message. CoAP keeps the header tiny (just 4 bytes) so that even the smallest devices can handle it.

42.3 Prerequisites

Before diving into this chapter, you should be familiar with:

  • CoAP Introduction - Understanding why CoAP exists and its design goals
  • Binary number representation (bits, bytes, hexadecimal notation)

42.4 Why CoAP’s Format Exists: Design Rationale

Before dissecting the format byte by byte, it is worth understanding why CoAP’s designers made each choice. Every field exists to solve a specific problem that HTTP headers solve poorly in constrained environments.

The core constraint: IEEE 802.15.4 (6LoWPAN) frames have a maximum transfer unit of 127 bytes. After MAC headers (23 bytes), 6LoWPAN compression (13 bytes), and UDP headers (8 bytes), only 83 bytes remain for the application. An HTTP GET request with minimal headers (GET /temp HTTP/1.1\r\nHost: sensor\r\n\r\n) consumes 39 bytes of text just for the request line and mandatory host header – nearly half the available payload before you even add content headers.

Design Decision HTTP Approach CoAP Approach Why CoAP Wins
Header size Variable text, 200-800 bytes typical Fixed 4 bytes binary Fits in 6LoWPAN with room for payload
Session state TCP connection + cookies Stateless with Token No RAM for connection tables on 32 KB MCUs
Metadata encoding Text headers (Content-Type: application/json) Binary option numbers (12 = Content-Format, value 50 = JSON) 2 bytes vs 30+ bytes for same information
Reliability TCP guarantees delivery (3-way handshake overhead) Per-message choice: CON or NON Sensor telemetry doesn’t need guaranteed delivery; firmware updates do
Request matching TCP connection scopes the response Token field (0-8 bytes) No connection setup overhead, works over UDP

Why 4 bytes and not 2 or 8? The header must encode four things: version (future-proofing), message type (reliability selection), token length (variable for flexibility), response code (method or status), and message ID (deduplication). Two bytes cannot encode all five fields with sufficient range. Eight bytes would waste precious 6LoWPAN space. Four bytes – 32 bits – is the minimum that encodes everything needed while leaving 79 of 83 available bytes for tokens, options, and payload.

Why Token instead of TCP sessions? A typical IoT gateway manages 50-200 sensors simultaneously. If each sensor maintained a TCP connection, the gateway would need 50-200 open sockets, each consuming ~1-4 KB of RAM for TCP state. On a gateway with 256 KB RAM, that is 10-80% of memory just for connection bookkeeping. CoAP’s token-based matching needs zero persistent state – the 0-8 byte token travels with the message, and the server can be truly stateless.

Why binary options instead of text headers? Consider telling the server the payload is JSON:

  • HTTP: Content-Type: application/json\r\n = 32 bytes of ASCII text
  • CoAP: Option delta=12, length=1, value=50 = 3 bytes of binary

That is a 10x reduction. Over a network where each transmitted byte costs battery (LoRa: ~0.5 mJ per byte at SF12), this savings directly translates to months of additional battery life.

42.5 The CoAP Message Format

Minimum Viable Understanding: CoAP Message Structure

Core Concept: A CoAP message consists of a 4-byte fixed header, optional token (0-8 bytes), zero or more options, and an optional payload. The fixed header contains version, message type, token length, method/response code, and message ID.

Why It Matters: Understanding the binary format is essential for debugging network captures, implementing CoAP libraries, and optimizing message sizes for constrained networks where every byte counts.

Key Takeaway: The 4-byte header efficiency is CoAP’s main advantage over HTTP - it encodes everything needed for request-response matching, reliability selection, and method identification in just 32 bits.

CoAP messages follow a compact binary format designed for efficiency:

Binary structure diagram showing CoAP message format with 4-byte header containing version, type, token length, code, and message ID fields, followed by variable-length token, delta-encoded options, and optional payload separated by 0xFF marker
Figure 42.1: CoAP message structure showing the 4-byte fixed header followed by optional token, options with delta compression, payload marker (0xFF), and application payload

42.6 The 4-Byte Fixed Header

The fixed header contains all essential message metadata in just 32 bits:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Ver| T |  TKL  |      Code     |          Message ID           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Field Bits Description
Ver 2 Version (always 01 for CoAP 1.0)
T 2 Type: CON (00), NON (01), ACK (10), RST (11)
TKL 4 Token Length (0-8 bytes)
Code 8 Method (0.xx) or Response (2.xx-5.xx)
Message ID 16 For deduplication and ACK matching

42.6.1 Header Fields Explained

Version (Ver): Always 01 binary (value 1) for CoAP RFC 7252.

Type (T): Determines reliability and message flow:

Type Binary Name Purpose
CON 00 Confirmable Reliable, requires ACK
NON 01 Non-confirmable Fire-and-forget
ACK 10 Acknowledgment Confirms CON receipt
RST 11 Reset Rejects message

Token Length (TKL): Number of token bytes (0-8). Tokens correlate responses to requests.

Code: Split into class (3 bits) and detail (5 bits), formatted as class.detail:

Class Range Meaning
0 0.01-0.04 Methods (GET, POST, PUT, DELETE)
2 2.01-2.05 Success responses
4 4.00-4.15 Client error
5 5.00-5.05 Server error

Message ID: 16-bit identifier for: - Matching ACK/RST to CON messages - Detecting duplicate messages (server caches for ~247 seconds)

42.6.2 When to Use Each Message Type: A Decision Guide

Choosing between CON and NON is one of the most important decisions in a CoAP implementation. The wrong choice either wastes battery on unnecessary retransmissions or silently loses critical commands.

IoT Scenario Recommended Type Rationale
Temperature reading every 5 min NON Missing one reading out of 288/day is acceptable. Retransmission doubles airtime and battery cost for negligible benefit.
Firmware update chunk CON Every 256-byte block must arrive. A single missing block corrupts the entire firmware image. The cost of retransmission is far less than the cost of a bricked device.
Smoke alarm triggered CON Life-safety event. The server must acknowledge receipt. Use exponential backoff (2s, 4s, 8s, 16s) per RFC 7252 Section 4.2.
Soil moisture (LoRaWAN, 1% duty cycle) NON Duty cycle constraints mean retransmission may violate regulatory limits. Design the application to tolerate 5-10% packet loss.
Door lock command CON User expects confirmation that the lock state changed. Without ACK, the app cannot show “Locked” reliably.
Parking sensor heartbeat NON Heartbeats are periodic. If one is lost, the next one arrives in minutes. Only escalate to CON if the server hasn’t heard from the device in 3x the heartbeat interval.
HVAC setpoint change CON Changing from 22C to 18C is a deliberate user action. The system must confirm the setpoint was applied, not silently ignored.

Rule of thumb: If losing the message would require human intervention to notice or correct, use CON. If the next periodic message will convey equivalent information, use NON.

Sammy the Sensor says: “Imagine sending a message in a bottle! The CoAP header is like the label you put on the bottle.”

The label tells you:

  • Type: “Please write back!” (CON) or “No reply needed” (NON)
  • Code: What you want - “Can I have the temperature?” (GET)
  • Message ID: A number so you know which bottle they’re answering

Just like how a bottle label is tiny compared to the letter inside, CoAP’s header is tiny (4 bytes!) compared to HTTP’s headers (hundreds of bytes!).

42.7 CoAP Method Codes

CoAP methods mirror HTTP methods but use numeric codes:

Code Method Description Idempotent
0.01 GET Retrieve resource Yes
0.02 POST Create resource or submit data No
0.03 PUT Update/replace resource Yes
0.04 DELETE Remove resource Yes
Idempotent Methods

An idempotent method produces the same result regardless of how many times it’s called: - GET /temperature returns current temperature (safe to retry) - PUT /led {"on": true} sets LED on (calling twice = same result) - DELETE /alarm/5 removes alarm 5 (calling twice = same result, even if already deleted)

POST is NOT idempotent - POST /log {"event": "click"} creates new entry each time.

Why this matters: When a CON message times out, CoAP can safely retry idempotent methods. For POST, the application must handle potential duplicates.

42.8 Response Codes

CoAP uses a structured response code system similar to HTTP:

42.8.1 Success (2.xx)

Code Name HTTP Equivalent Use Case
2.01 Created 201 Created POST created new resource
2.02 Deleted 200 OK (for DELETE) Resource removed
2.03 Valid 304 Not Modified Cached response still valid
2.04 Changed 200 OK (for PUT) Resource updated
2.05 Content 200 OK GET successful with payload

42.8.2 Client Error (4.xx)

Code Name HTTP Equivalent Cause
4.00 Bad Request 400 Malformed request
4.01 Unauthorized 401 Missing authentication
4.03 Forbidden 403 Access denied
4.04 Not Found 404 Resource doesn’t exist
4.05 Method Not Allowed 405 Wrong method for resource
4.12 Precondition Failed 412 ETag mismatch
4.15 Unsupported Content-Format 415 Unknown payload format

42.8.3 Server Error (5.xx)

Code Name HTTP Equivalent Cause
5.00 Internal Server Error 500 Server crashed
5.01 Not Implemented 501 Method not supported
5.02 Bad Gateway 502 Proxy error
5.03 Service Unavailable 503 Server overloaded
5.04 Gateway Timeout 504 Proxy timeout

42.9 Token: Request-Response Matching

The Token correlates responses to requests, especially important when multiple requests are in flight:

Client sends: GET /temperature, Token=0xAB12, MsgID=0x5678
Client sends: GET /humidity, Token=0xCD34, MsgID=0x5679

Server sends: 2.05 Content, Token=0xCD34, "65%"  (humidity response)
Server sends: 2.05 Content, Token=0xAB12, "22.5" (temperature response)

Token vs. Message ID:

Aspect Token Message ID
Purpose Match response to request Deduplication, ACK matching
Scope Application-level Transport-level
Length 0-8 bytes (TKL field) Fixed 16 bits
Persistence Across retransmissions Changes on each transmission
Generated by Client Client

Token Security Consideration

In secure deployments (DTLS), tokens should be unpredictable (randomly generated) to prevent response spoofing attacks. A malicious actor who can predict tokens could inject fake responses.

# BAD: Sequential tokens (predictable)
token = counter
counter += 1

# GOOD: Random tokens (unpredictable)
import os
token = os.urandom(4)  # 4 random bytes

42.10 CoAP Options

Options carry metadata similar to HTTP headers but use efficient binary encoding:

42.10.1 Common Options

Option Number Name Length Purpose
3 Uri-Host String Target host
7 Uri-Port 0-2 bytes Target port
11 Uri-Path String Path segments (/sensors/temp)
12 Content-Format 0-2 bytes Payload MIME type
14 Max-Age 0-4 bytes Cacheability (seconds)
17 Accept 0-2 bytes Acceptable response format
35 Proxy-Uri String For proxied requests

42.10.2 Option Delta Encoding

Options are sorted by number and use delta encoding to minimize bytes:

Option format:
+----+----+----------------+----------------+
| OD | OL | [Extended OD]  | Value          |
+----+----+----------------+----------------+
  4    4    0, 1, or 2       Variable
bits  bits   bytes

OD = Option Delta (difference from previous option number)
OL = Option Length

Example: Encoding Uri-Path /sensors/temp

Option 1: Uri-Path = "sensors"
  Delta = 11 (first option, no previous)
  Length = 7 ("sensors")
  Encoded: 0xB7 | "sensors"

Option 2: Uri-Path = "temp"
  Delta = 0 (11 - 11 = 0, same option type)
  Length = 4 ("temp")
  Encoded: 0x04 | "temp"

Total: 13 bytes vs HTTP "GET /sensors/temp HTTP/1.1\r\n" = 27 bytes

42.10.3 Content-Format Codes

The Content-Format option specifies the payload MIME type:

Code MIME Type Description
0 text/plain Simple text
40 application/link-format Resource discovery
41 application/xml XML data
42 application/octet-stream Binary data
47 application/exi Efficient XML
50 application/json JSON data
60 application/cbor Compact Binary (CBOR)
CBOR vs JSON for IoT

CBOR (Concise Binary Object Representation) is to JSON what CoAP is to HTTP - a compact binary alternative:

# JSON (42 bytes)
{"device":"temp42","value":23.5,"unit":"C"}

# CBOR (~20 bytes) - same data, half the size
A3 66 64 65 76 69 63 65 66 74 65 6D 70 34 32...

When to use:

  • CBOR: Production deployments, battery-powered devices, constrained networks
  • JSON: Development, debugging, cloud integration with JSON-native APIs

42.11 Worked Example: Parsing a CoAP Message

Worked Example: Decoding a GET Request

Raw bytes (hexadecimal):

44 01 5A F2 AB CD 00 00 B5 68 65 6C 6C 6F

Step 1: Parse fixed header (4 bytes)

Byte 1: 0x44 = 0100 0100 binary - Ver = 01 = Version 1 (valid CoAP) - T = 00 = CON (Confirmable) - TKL = 0100 = 4 (4-byte token)

Byte 2: 0x01 = Code - Class = 0, Detail = 01 = 0.01 = GET

Bytes 3-4: 0x5AF2 = Message ID = 23282

Step 2: Parse token (TKL = 4 bytes)

Bytes 5-8: AB CD 00 00 = Token = 0xABCD0000

Step 3: Parse options

Byte 9: 0xB5 = 1011 0101 - Option Delta = 11 (decimal) = Uri-Path - Option Length = 5

Bytes 10-14: 68 65 6C 6C 6F = ASCII “hello”

Decoded message:

Field Value
Version 1
Type CON (Confirmable)
Method GET
Message ID 23282
Token 0xABCD0000
Uri-Path /hello

Full URI: coap://server/hello

42.11.1 Worked Example: Complete Sensor Reading – CoAP vs HTTP

Worked Example: GET /sensors/temp with JSON Response

This example traces a complete request-response pair for reading a temperature sensor, showing every byte in the CoAP message and comparing to the equivalent HTTP exchange.

Scenario: An ESP32 gateway requests the current temperature from a sensor node at coap://sensor.local/sensors/temp, expecting a JSON response.


CoAP Request (21 bytes total):

Offset  Hex          Binary             Field
------  ---          ------             -----
 0      44           0100 0100          Ver=1, T=CON, TKL=4
 1      01           0000 0001          Code=0.01 (GET)
 2-3    7D 34        0111 1101 0011 0100  MsgID=32052
 4-7    A1 B2 C3 D4  (random bytes)     Token=0xA1B2C3D4
 8      B7           1011 0111          OptDelta=11(Uri-Path), OptLen=7
 9-15   73 65 6E 73 6F 72 73            "sensors"
16      04           0000 0100          OptDelta=0(Uri-Path), OptLen=4
17-20   74 65 6D 70                     "temp"

Total: 21 bytes on the wire (plus 8 bytes UDP header = 29 bytes from IP layer).


Equivalent HTTP Request (minimum):

GET /sensors/temp HTTP/1.1\r\n         (27 bytes)
Host: sensor.local\r\n                 (20 bytes)
Accept: application/json\r\n           (26 bytes)
Connection: keep-alive\r\n             (24 bytes)
\r\n                                   (2 bytes)

Total: 99 bytes minimum (plus 20 bytes TCP header + 20 bytes TCP options typical = 139 bytes from IP layer). Real-world HTTP requests with cookies, user-agent, and other headers typically reach 300-800 bytes.


CoAP Response (35 bytes total):

Offset  Hex          Field
------  ---          -----
 0      64           Ver=1, T=ACK, TKL=4
 1      45           Code=2.05 (Content)
 2-3    7D 34        MsgID=32052 (matches request)
 4-7    A1 B2 C3 D4  Token=0xA1B2C3D4 (matches request)
 8      C1           OptDelta=12(Content-Format), OptLen=1
 9      32           Value=50 (application/json)
10      FF           Payload marker
11-34   {"temp":22.5,"unit":"C"}       (24 bytes JSON payload)

Total: 35 bytes. The ACK + response is piggybacked – one message serves as both “I received your request” and “here is the data.”


Size Comparison Summary:

Component CoAP HTTP/1.1 Savings
Request 21 bytes 99 bytes (min) 4.7x smaller
Response 35 bytes ~180 bytes (headers + body) 5.1x smaller
Transport overhead 8 bytes (UDP) 40+ bytes (TCP) 5x smaller
Total exchange 64 bytes ~320 bytes 5x smaller
Connection setup 0 messages 3 messages (TCP handshake) No handshake

On a LoRa link at SF10 (EU868), each byte costs approximately 1.2 ms of airtime. The CoAP exchange takes ~77 ms airtime vs ~384 ms for HTTP – a difference that matters when duty cycle limits you to 1% transmission time.

We can calculate the exact battery impact over the device lifetime. The total energy consumption for protocol overhead is:

\[E_{\text{total}} = \text{bytes} \times \text{energy per byte} \times \text{messages per day} \times \text{days}\]

For CoAP over 5 years (1,825 days) at 96 readings/day:

\[E_{\text{CoAP}} = 64 \text{ bytes} \times 0.5 \text{ mJ/byte} \times 96 \times 1{,}825 = 5{,}616{,}000 \text{ mJ} \approx 5{,}616 \text{ J}\]

For HTTP over the same period:

\[E_{\text{HTTP}} = 320 \text{ bytes} \times 0.5 \text{ mJ/byte} \times 96 \times 1{,}825 = 28{,}080{,}000 \text{ mJ} \approx 28{,}080 \text{ J}\]

This means HTTP consumes \(28{,}080 / 5{,}616 = 5\times\) more energy just for protocol messaging. With 2 AA batteries providing roughly 10,000 J total capacity, the savings directly translates to multi-year battery life differences.

Battery impact: At 0.5 mJ per byte (typical LoRa SF12), the CoAP exchange costs 32 mJ vs 160 mJ for HTTP per reading. Over 96 readings per day for 5 years (175,200 total readings), CoAP consumes ~5,616 J vs ~28,080 J just for protocol messaging – a 5x difference. With 2 AA batteries providing roughly 10,000 J total capacity, HTTP protocol overhead alone would exhaust nearly three battery sets over that period, while CoAP protocol overhead consumes just over half of one set.

Knowledge Check: Matching and Sequencing

Test your understanding of CoAP concepts and message processing order.

42.12 Interactive Tools

42.13 Summary

The CoAP message format achieves remarkable efficiency:

  • 4-byte fixed header encodes version, type, code, and message ID in just 32 bits
  • Tokens (0-8 bytes) correlate responses to requests without needing TCP connection state
  • Delta-encoded options minimize metadata overhead – a Uri-Path option costs 1+N bytes vs HTTP’s full URL text
  • Response codes mirror HTTP semantics (2.xx success, 4.xx client error, 5.xx server error)
  • A complete GET request fits in 14 bytes vs 68+ bytes for equivalent HTTP – a 5x reduction that fits within 6LoWPAN’s 83-byte application payload

Understanding this format is essential for debugging network captures, implementing CoAP libraries, and optimizing message sizes for constrained networks.

42.14 How It Works: Option Delta Encoding Saves Bytes

Delta encoding is a brilliant space-saving technique. Here’s why it matters:

Without delta encoding (naive approach):

Each option would need to specify its full option number:

Option 1: Number=11 (Uri-Path), Value="sensors" → 1 byte (number) + 1 byte (length) + 7 bytes (value) = 9 bytes
Option 2: Number=11 (Uri-Path), Value="temp"    → 1 byte (number) + 1 byte (length) + 4 bytes (value) = 6 bytes
Total: 15 bytes

With delta encoding (CoAP’s approach):

Each option specifies only the difference from the previous option number:

Option 1: Delta=11 (first option, so 0+11=11), Value="sensors" → 1 byte (delta+length) + 7 bytes = 8 bytes
Option 2: Delta=0 (11+0=11, same option type), Value="temp"    → 1 byte (delta+length) + 4 bytes = 5 bytes
Total: 13 bytes (2 bytes saved)

Why this matters for /sensors/temp/reading/current:

Without delta (5 Uri-Path segments): - 5 segments × 1 byte option number = 5 bytes wasted

With delta (5 segments, all consecutive option 11): - Option 1: delta=11 (first) - Options 2-5: delta=0 (same option type) - Bytes saved: 4 bytes (only first segment pays the full 11-byte delta)

Extended savings with long URIs:

For a resource like /api/v2/devices/sensor42/temperature/readings/latest: - 7 path segments - Without delta: 7 bytes for option numbers - With delta: 11 (first) + 0×6 (rest) = only the first segment specifies option 11 - Savings: 6 bytes

Why sorting matters:

Options must be sorted by number for delta to work:

Correct order: Uri-Path (11), Uri-Path (11), Content-Format (12)
  Delta encoding: 11, 0, 1 (total delta bits: 4+4+4 = 12 bits)

Wrong order: Content-Format (12), Uri-Path (11), Uri-Path (11)
  Delta encoding: 12, -1 (INVALID - can't have negative delta!)

CoAP mandates sorted options so delta is always non-negative, enabling compact 4-bit delta fields.

42.15 Concept Relationships

This chapter connects binary protocol details to practical implementation:

Foundation Knowledge:

Message Structure:

  • 4-byte fixed header (version, type, token length, code, message ID)
  • Variable-length token (0-8 bytes) for request-response matching
  • Delta-encoded options (metadata like Uri-Path, Content-Format)
  • Optional payload (sensor data, JSON, CBOR)

Related Protocols:

  • HTTP Headers - Contrast with text-based encoding
  • MQTT Message Format - Alternative binary protocol
  • Protocol Buffers - Similar binary encoding philosophy

Implementation:

42.16 See Also

Specifications:

Tools:

Binary Encoding:

Debugging:

42.17 What’s Next

Now that you can decode and construct CoAP messages, these chapters build directly on that foundation:

Chapter Focus Why Read It
CoAP Message Types CON, NON, ACK, RST reliability mechanics Understand exactly how retransmission timers, exponential backoff, and duplicate detection operate at the message level
CoAP Observe Extension Server-push subscription model See how a single GET with the Observe option transforms CoAP into a pub/sub system for real-time sensor streaming
CoAP Block-Wise Transfers Fragmentation of large payloads Learn the Block1/Block2 options that split firmware images and large datasets across multiple CoAP messages
CoAP Implementation Labs Hands-on Python/ESP32 coding Apply the binary format knowledge by sending and receiving real CoAP messages with aiocoap and MicroPython
CoAP Security with DTLS Encryption and authentication Secure the tokens and payloads you now understand at the binary level using DTLS record layer wrapping
CoAP Fundamentals and Architecture Full CoAP chapter index Navigate all CoAP topics in sequence or jump to the specific area you need