32  IoT Protocols Lab: CoAP vs MQTT

Key Concepts
  • CoAP GET/PUT/POST/DELETE: RESTful methods mirroring HTTP, carried over UDP with minimal overhead
  • MQTT Broker: A server that receives all published messages and routes them to subscribed clients; Mosquitto and HiveMQ are common choices
  • Retained Message: An MQTT message stored by the broker and delivered immediately to new subscribers, providing last-known-value semantics
  • Will Message: An MQTT message the broker sends on behalf of a client that disconnects unexpectedly, useful for device-offline alerts
  • CoAP Observe: An extension that allows a CoAP client to subscribe to resource updates, similar to MQTT subscriptions
  • Wireshark Dissector: A plugin that decodes CoAP or MQTT packets for inspection; essential for debugging protocol interactions
  • Loopback Testing: Running both client and broker/server on the same machine (localhost) to isolate protocol behaviour from network issues

32.1 In 60 Seconds

CoAP runs over UDP with a 4-byte base header, making it ideal for constrained devices with direct request-response communication. MQTT runs over TCP with a 2-byte minimum header, excelling at scalable publish-subscribe telemetry through a broker. This lab compares their architectures, overhead, QoS mechanisms, and transport layer trade-offs to help you choose the right protocol for your deployment.

32.2 Learning Objectives

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

  • Compare CoAP and MQTT architectures: Differentiate request-response and publish-subscribe patterns
  • Calculate protocol overhead: Compute header sizes and payload efficiency for each protocol
  • Select appropriate protocols: Justify CoAP or MQTT choice based on device constraints and use cases
  • Analyse transport layer trade-offs: Determine when UDP (CoAP) versus TCP (MQTT) is appropriate
  • Apply QoS levels: Match reliability requirements to MQTT QoS or CoAP confirmable messages

What is this chapter? Deep comparison of the two most popular IoT application protocols: CoAP and MQTT.

Why it matters:

  • Wrong protocol choice wastes battery life or misses reliability requirements
  • CoAP excels for constrained devices with direct communication
  • MQTT excels for scalable telemetry with broker-based distribution

Prerequisites:

32.3 Application Layer Protocols

Traditional application protocols (HTTP, XMPP) are resource-demanding and not suitable for constrained IoT devices.

32.3.1 Why Not Use HTTP?

HTTP (Hypertext Transfer Protocol):

Overhead:

HTTP GET Request (minimum):
GET / HTTP/1.1\r\n
Host: example.com\r\n
\r\n

Size: ~40 bytes (just headers, no data)

HTTP Response:
HTTP/1.1 200 OK\r\n
Content-Type: application/json\r\n
Content-Length: 10\r\n
\r\n
{"temp":25}

Size: ~70 bytes headers + 10 bytes data = 80 bytes
Total exchange: ~120 bytes

Issues for IoT:

  • Verbose: Text-based, human-readable (wasteful for M2M)
  • TCP-based: Connection overhead
  • Stateful: Requires connection management
  • No pub/sub: Request-response only

When HTTP is OK:

  • Gateways: IoT gateway to cloud (mains-powered, reliable network)
  • RESTful APIs: Cloud services
  • Infrequent: Configuration, firmware updates

32.3.2 IoT-Specific Application Protocols

The two most popular IoT application protocols: 1. CoAP (Constrained Application Protocol) 2. MQTT (Message Queue Telemetry Transport)

32.4 CoAP vs MQTT Comparison

Comparison of CoAP and MQTT architectures showing CoAP's request-response pattern between sensor and gateway using UDP with 4-byte headers versus MQTT's publish-subscribe pattern with broker mediating between sensor publisher and dashboard subscriber using TCP
Figure 32.1: CoAP vs MQTT Communication Pattern Comparison

32.4.1 Detailed Comparison

Aspect CoAP MQTT
Model Request-Response (1-to-1) Publish-Subscribe (many-to-many)
Transport UDP (connectionless) TCP (connection-oriented)
Messaging RESTful (GET, POST, PUT, DELETE) Pub/Sub topics
Overhead Low (4-byte header) Medium (2-byte fixed + variable)
Reliability Optional (Confirmable messages) QoS levels (0, 1, 2)
Power Very low (UDP, no broker connection) Higher (TCP, always connected)
Real-Time Excellent (UDP, no broker hop) Good (broker adds latency)
Scalability Moderate (P2P connections) Excellent (broker decouples)
Discovery Built-in (Resource Discovery) None (app-level)
Security DTLS TLS (MQTTS)
Multicast Yes (UDP multicast) No (TCP point-to-point)
Message Size Small (UDP constraints) Flexible (TCP streaming)
Complexity Lower (simple devices) Higher (broker required)
Best For Constrained devices, real-time Data collection, dashboards

32.4.2 CoAP (Constrained Application Protocol)

CoAP Characteristics

RESTful Design:

  • Mirrors HTTP (GET, POST, PUT, DELETE)
  • URI-based resources (coap://example.com/sensor/temp)
  • Content types (JSON, CBOR, XML)

Built for UDP:

  • Connectionless (low overhead)
  • Optional reliability (Confirmable vs Non-Confirmable)
  • Multicast support (discover devices)

Lightweight:

  • 4-byte header (vs 40+ HTTP)
  • Binary protocol (efficient parsing)
  • Small code footprint (~10 KB)

Features:

  • Resource discovery: /.well-known/core
  • Observe: Subscribe to resource changes
  • Block transfer: Large data in chunks

Use Cases:

  • Sensor readings (temperature, humidity)
  • Actuator control (lights, locks)
  • Device configuration
  • Resource-constrained networks

CoAP Message Example:

GET /sensor/temp
Host: fe80::1234

CoAP Header (4 bytes):
- Version (2b), Type (2b), Token Length (4b), Code (8b): 2 bytes
- Message ID: 2 bytes

Total: ~20 bytes (with URI-Path options)

vs HTTP: ~40 bytes minimum

32.4.3 MQTT (Message Queue Telemetry Transport)

MQTT Characteristics

Publish-Subscribe:

  • Producers (sensors) publish to topics
  • Consumers (apps) subscribe to topics
  • Broker decouples publishers and subscribers

Designed for Telemetry:

  • Many sensors → Few subscribers
  • Persistent connections (TCP)
  • Quality of Service (QoS) levels

Lightweight (for TCP): - 2-byte fixed header + variable header - Binary protocol - Compact topic names

QoS Levels:

  • QoS 0: At most once (fire and forget)
  • QoS 1: At least once (acknowledged)
  • QoS 2: Exactly once (guaranteed)

Features:

  • Retained messages: Last value available to new subscribers
  • Last Will and Testament: Notify on disconnect
  • Clean session: Persistent vs ephemeral

Use Cases:

  • Telemetry collection (many sensors → cloud)
  • Dashboard updates (real-time monitoring)
  • Command distribution (cloud → devices)
  • Mobile apps (unreliable networks)

MQTT Topic Example:

Publishers:
- Sensor 1: Publish to "home/bedroom/temperature"
- Sensor 2: Publish to "home/kitchen/temperature"

Subscribers:
- Dashboard: Subscribe to "home/+/temperature" (all rooms)
- Logger: Subscribe to "home/#" (all topics under home)

Broker: Routes messages from publishers to subscribers

CoAP/UDP and MQTT/TCP create dramatically different overhead ratios. Let’s quantify the energy and bandwidth impact:

\[ \text{Overhead Ratio} = \frac{\text{Total Packet Size}}{\text{Payload Size}} \]

Worked example: 4-byte temperature reading with full protocol stacks

CoAP/UDP/IPv6 (uncompressed):

  • Headers: 25 (802.15.4) + 40 (IPv6) + 8 (UDP) + 4 (CoAP) = 77 bytes
  • Total: 77 + 4 = 81 bytes, overhead ratio = 81/4 = 20.25×

CoAP/UDP/6LoWPAN (compressed):

  • Headers: 25 (802.15.4) + 7 (6LoWPAN dispatch + compressed IPv6) + 4 (compressed UDP) + 4 (CoAP) = 40 bytes
  • Total: 40 + 4 = 44 bytes, overhead ratio = 44/4 = 11.0x
  • Savings vs uncompressed: 81 - 44 = 37 bytes saved per packet

MQTT/TCP/IPv6 (uncompressed):

  • Headers: 25 (802.15.4) + 40 (IPv6) + 20 (TCP) + 2 (MQTT) = 87 bytes
  • Total: 87 + 4 = 91 bytes, overhead ratio = 91/4 = 22.75×

Energy impact at 250 kbps with 20 mA TX current (radio-on energy only, excluding sleep and processing current):

  • CoAP/6LoWPAN (44 bytes): \(t = \frac{44 \times 8}{250{,}000} = 1.41\text{ ms}\), \(E = 20\text{ mA} \times \frac{1.41\text{ ms}}{3{,}600{,}000\text{ ms/h}} = 7.8\text{ }\mu\text{Ah}\)
  • MQTT/IPv6 (91 bytes): \(t = \frac{91 \times 8}{250{,}000} = 2.91\text{ ms}\), \(E = 20\text{ mA} \times \frac{2.91\text{ ms}}{3{,}600{,}000\text{ ms/h}} = 16.2\text{ }\mu\text{Ah}\)

The key takeaway is the 2.1x ratio: uncompressed MQTT/TCP/IPv6 transmits roughly twice the bytes per reading compared to 6LoWPAN-compressed CoAP/UDP, directly translating to proportionally higher radio energy consumption per packet.

Try It: Protocol Overhead Calculator

Adjust the payload size and protocol stack to compare CoAP vs MQTT overhead ratios interactively.

32.4.4 CoAP vs MQTT: When to Use

Selection Guide

Use CoAP when:

  • Constrained devices: Limited RAM/CPU (< 32 KB RAM)
  • Real-time: Low latency critical
  • Multicast: Need to address multiple devices simultaneously
  • Power-constrained: Battery-powered sensors
  • Direct M2M: Machine-to-machine without broker
  • RESTful: Web-style API desired
  • Example: Temperature sensor reporting to gateway

Use MQTT when:

  • Scalability: Many publishers, many subscribers
  • Reliable network: TCP acceptable (Wi-Fi, Ethernet, cellular)
  • Guaranteed delivery: QoS critical
  • Decoupling: Publishers/subscribers don’t know each other
  • Dashboard/monitoring: Real-time data visualization
  • Cloud integration: MQTT brokers widely supported
  • Example: Factory sensors → cloud → monitoring dashboard

Consider Hybrid:

  • CoAP for sensor → gateway (constrained network)
  • MQTT for gateway → cloud (reliable network)

Objective: See MQTT publish-subscribe in action on an ESP32. The device publishes simulated sensor data to a topic and subscribes to a command topic, demonstrating the broker-mediated pattern described above.

Code to Try:

#include <WiFi.h>
#include <PubSubClient.h>

const char* ssid = "Wokwi-GUEST";
const char* password = "";
const char* mqtt_server = "broker.hivemq.com";

WiFiClient espClient;
PubSubClient client(espClient);
unsigned long lastPublish = 0;

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("[RECEIVED] Topic: ");
  Serial.print(topic);
  Serial.print(" | Payload: ");
  for (unsigned int i = 0; i < length; i++) Serial.print((char)payload[i]);
  Serial.println();
}

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
  Serial.println("\nWi-Fi connected");

  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);

  while (!client.connected()) {
    String id = "ESP32-" + String(random(0xffff), HEX);
    if (client.connect(id.c_str())) {
      Serial.println("MQTT connected to broker.hivemq.com");
      // Subscribe to a command topic
      client.subscribe("iotclass/lab/commands");
      Serial.println("Subscribed to: iotclass/lab/commands");
    } else {
      delay(2000);
    }
  }
}

void loop() {
  client.loop();

  if (millis() - lastPublish > 3000) {
    lastPublish = millis();
    float temp = 20.0 + random(-50, 50) / 10.0;
    float hum = 50.0 + random(-100, 100) / 10.0;
    char msg[64];
    snprintf(msg, sizeof(msg), "{\"temp\":%.1f,\"humidity\":%.1f}", temp, hum);

    client.publish("iotclass/lab/sensors/env", msg);
    Serial.print("[PUBLISHED] ");
    Serial.println(msg);
  }
}

What to Observe:

  1. The ESP32 publishes to iotclass/lab/sensors/env every 3 seconds – this is the pub/sub pattern MQTT uses
  2. It also subscribes to iotclass/lab/commands – any message published to that topic by another client will appear
  3. The broker (HiveMQ) routes messages between publishers and subscribers without them knowing each other
  4. Compare this with CoAP’s direct request-response: MQTT uses a broker intermediary, adding latency but enabling many-to-many communication

32.6 Knowledge Check

Scenario: A battery-powered temperature sensor sends 10-byte readings every 60 seconds via MQTT. The team debates MQTT QoS levels.

Step 1: Understand QoS message flows

QoS 0 (At most once):
  Client → Broker: PUBLISH (14 bytes: 2 fixed + 2 var + 10 payload)
  Total: 1 message (14 bytes)

QoS 1 (At least once):
  Client → Broker: PUBLISH (16 bytes: 2 fixed + 4 var + 10 payload)
  Broker → Client: PUBACK (4 bytes)
  Total: 2 messages (20 bytes)

QoS 2 (Exactly once):
  Client → Broker: PUBLISH (16 bytes)
  Broker → Client: PUBREC (4 bytes)
  Client → Broker: PUBREL (4 bytes)
  Broker → Client: PUBCOMP (4 bytes)
  Total: 4 messages (28 bytes)

Step 2: Compare relative data volume

QoS 0: 14 bytes transmitted (TX only)
QoS 1: 20 bytes transmitted (16 TX + 4 RX) → 1.43× QoS 0
QoS 2: 28 bytes transmitted (20 TX + 8 RX) → 2.00× QoS 0

Since radio energy is roughly proportional to bytes transmitted, the QoS level directly scales energy cost per message.

Step 3: Quantify the daily impact

1,440 readings/day (every 60 sec)

QoS 0: 1,440 × 14 = 20,160 bytes/day (baseline)
QoS 1: 1,440 × 20 = 28,800 bytes/day (43% more radio traffic)
QoS 2: 1,440 × 28 = 40,320 bytes/day (100% more radio traffic)

QoS 2 doubles the radio traffic compared to QoS 0.

Step 4: Evaluate actual reliability need

Question: What happens if a single temperature reading is lost?

Data Pattern:
  60-second intervals → 1,440 readings/day
  QoS 0 with 1% loss → 14 readings lost/day
  Result: Dashboard shows 23.5°C at 10:00, missing at 10:01, then 23.6°C at 10:02

Impact on monitoring dashboard: Negligible (next reading in 60 seconds)
Impact on trend analysis: Negligible (still 1,426 points/day for smoothing)
Impact on alerting: Potentially critical if loss occurs during temperature spike

Decision Matrix:

Use Case QoS Choice Rationale
General monitoring QoS 0 60-second intervals + 1% loss = still 1,426 readings/day, trends clear
Critical alerting QoS 1 43% radio traffic increase acceptable for ensured delivery of threshold alerts
Billing/compliance QoS 2 100% radio traffic increase acceptable for exactly-once financial records

The Fix: Use QoS 0 for routine telemetry, QoS 1 for threshold alerts via topic separation:

# Sensor publishes to two topics
mqtt.publish("sensors/temp/raw", 23.5, qos=0)  # Routine data

if temp > ALERT_THRESHOLD:
    mqtt.publish("alerts/temp/high", 23.5, qos=1)  # Critical alert

Result:

  • 99% of messages use QoS 0 (saves energy)
  • 1% of messages use QoS 1 (when it matters)
  • Radio energy: essentially the same as pure QoS 0

Lesson: QoS 2 doubles the radio traffic of QoS 0 per message exchange. Only use it when exact duplicate prevention is financially or legally critical (billing, financial transactions). For monitoring, QoS 1 or even QoS 0 is usually sufficient.

Try It: MQTT QoS Traffic Comparison

Adjust the transmission interval and payload size to see how QoS level affects daily radio traffic volume.

32.7 Concept Relationships

Understanding how CoAP and MQTT relate to other IoT concepts deepens your protocol selection skills:

Related Concepts:

  • 6LoWPAN compression works with both protocols but benefits CoAP more due to its UDP transport and compact headers
  • QoS mechanisms appear differently: MQTT has built-in QoS levels (0/1/2) while CoAP uses confirmable messages with exponential backoff
  • Broker architecture (MQTT) vs direct addressing (CoAP) represents fundamentally different scaling patterns
  • RESTful semantics in CoAP mirror HTTP methods (GET/POST/PUT/DELETE), enabling familiar API design on constrained devices
  • TCP vs UDP transport determines reliability model: MQTT relies on TCP guarantees, CoAP implements application-level reliability over UDP

Prerequisite Knowledge:

  • Transport Protocols - Understanding UDP vs TCP trade-offs clarifies why CoAP uses UDP for efficiency
  • IPv6 and 6LoWPAN - Header compression fundamentals that enable both protocols on constrained networks

Builds Foundation For:

32.8 See Also

Protocol Deep Dives:

Protocol Comparison:

Implementation Guides:

Common Pitfalls

Connecting an MQTT client before the broker is running produces a “connection refused” error that beginners mistake for a code bug. Fix: always start the broker first and verify it is listening on port 1883 before launching clients.

Publishing a message and then subscribing means the subscriber misses messages sent before it connected (unless the message was retained). Fix: subscribe first, then publish; or use retained messages for state values.

CoAP is designed for UDP. Some libraries silently fall back to TCP, changing timing and reliability behaviour. Fix: explicitly specify UDP transport and verify with Wireshark.

If a lab exercise pauses for 60+ seconds without sending messages, the broker drops the client as disconnected. Fix: set keepalive=120 seconds or send periodic PINGREQ messages during idle periods.

32.9 Summary

This chapter compared the two dominant IoT application layer protocols:

  • HTTP is too verbose for IoT with 40+ byte headers and TCP connection overhead, making it unsuitable for constrained devices
  • CoAP provides RESTful semantics over UDP with a 4-byte header, multicast support, and optional reliability via confirmable messages
  • MQTT enables scalable pub/sub telemetry with broker decoupling, QoS levels (0/1/2), and features like retained messages and Last Will
  • Choose CoAP for constrained devices requiring low latency, multicast, or direct M2M communication with minimal power consumption
  • Choose MQTT for scalable data collection where many publishers feed dashboards and cloud services through a central broker
  • Hybrid architectures work well: CoAP from sensors to gateway, MQTT from gateway to cloud

32.10 What’s Next

Direction Chapter Description
Next IoT Protocols Lab: Selection Framework Decision tree for protocol selection
Previous IoT Protocols Lab: IPv6 and 6LoWPAN Network layer addressing and compression
Deep Dive CoAP Fundamentals Detailed CoAP protocol architecture
Deep Dive MQTT Fundamentals Comprehensive MQTT protocol coverage