42 CoAP Message Format and Structure
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
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:
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 |
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 |
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 bytes42.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 (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
42.11.1 Worked Example: Complete Sensor Reading – CoAP vs HTTP
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:
- CoAP Introduction - Why compact encoding matters for constrained devices
- Binary Number Systems - Bits, bytes, hexadecimal notation
- Networking Basics - Headers, payloads, encapsulation
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:
- CoAP Implementation Labs - Using aiocoap’s Message class
- Wireshark CoAP Analysis - Decoding captures
- Binary Protocol Debugging - Common mistakes
42.16 See Also
Specifications:
- RFC 7252 Section 3 - Message format details
- CoAP Option Numbers Registry - Official option list
- Content-Format Registry - MIME type codes
Tools:
- Wireshark CoAP Dissector - Network capture analysis
- Online CoAP Parser - Hex to human-readable
- coap-client Manual - CLI testing tool
Binary Encoding:
- CBOR (RFC 8949) - Efficient payload encoding
- Variable-Length Encoding - Delta encoding principles
- Bit Manipulation - Working with binary data
Debugging:
- Common CoAP Errors - 4.xx/5.xx response code meanings
- Token Matching Issues - Request-response correlation
- Option Parsing Pitfalls - Delta encoding mistakes
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 |