IoT Communication Protocols — MQTT, CoAP & HTTP Guide
In this tutorial, you'll learn about IoT Communication Protocols. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
IoT communication protocols define how devices exchange data across networks, with MQTT, CoAP, and HTTP each optimized for different constraints — bandwidth, power, latency, and reliability — in the Internet of Things ecosystem.
What You'll Learn
You'll understand the differences between MQTT, CoAP, and HTTP/2 for IoT, when to choose each protocol, implement pub/sub with MQTT, request-response with CoAP, and benchmark protocol performance.
Why IoT Protocols Matter
IoT devices range from battery-powered sensors (lasting years on a coin cell) to mains-powered gateways. Choosing the wrong protocol wastes power, bandwidth, or development time. At DodaTech, our IoT Security scanner uses CoAP for sensor data (2-byte overhead) and MQTT for alerts (reliable delivery) — each protocol matched to its task.
Real-World Use Case
A smart building with 2,000 sensors initially uses HTTP polling. Each sensor sends 1KB every minute = 2MB/min from the gateway. Switching to MQTT with QoS 0 reduces overhead to 4 bytes per message, cutting bandwidth by 99.6% and extending sensor battery life from 3 to 18 months.
Protocol Comparison
| Feature | MQTT | CoAP | HTTP/2 |
|---|---|---|---|
| Transport | TCP | UDP | TCP |
| Header Size | 2 bytes | 4 bytes | 200+ bytes |
| Model | Pub/Sub | Request/Response | Request/Response |
| QoS | 0, 1, 2 | Confirmable, Non-confirmable | None |
| Security | TLS | DTLS | TLS |
| Best For | Reliable messaging | Constrained devices | Rich APIs |
MQTT Implementation
MQTT uses a broker to decouple publishers and subscribers:
import paho.mqtt.client as mqtt
import json
import time
# Sensor data publisher
def on_connect(client, userdata, flags, rc):
print(f"Connected with result code {rc}")
client = mqtt.Client()
client.on_connect = on_connect
client.connect("broker.emqx.io", 1883, 60)
temperature = 22.5
humidity = 55
while True:
payload = json.dumps({
"device_id": "sensor-01",
"temperature": temperature,
"humidity": humidity,
"timestamp": time.time()
})
# Publish with QoS 1 (at least once)
result = client.publish("building/floor1/temperature",
payload, qos=1)
if result.rc == mqtt.MQTT_ERR_SUCCESS:
print(f"Published: {payload}")
else:
print(f"Publish failed: {result.rc}")
temperature += 0.1
time.sleep(10)
Expected output: Every 10 seconds, the script publishes JSON sensor data to the MQTT broker. QoS 1 guarantees delivery at least once.
MQTT Subscriber
import paho.mqtt.client as mqtt
def on_message(client, userdata, msg):
print(f"Topic: {msg.topic}")
print(f"Payload: {msg.payload.decode()}")
# Parse and process
import json
data = json.loads(msg.payload.decode())
if data["temperature"] > 30:
print("ALERT: High temperature detected!")
subscriber = mqtt.Client()
subscriber.on_message = on_message
subscriber.connect("broker.emqx.io", 1883, 60)
subscriber.subscribe("building/floor1/#", qos=1)
print("Listening for sensor data...")
subscriber.loop_forever()
Expected output: The subscriber receives all messages matching building/floor1/# and prints both the raw payload and a temperature alert if threshold is exceeded.
CoAP Implementation
CoAP uses UDP with retransmission for constrained devices:
from aiocoap import *
import asyncio
import json
# CoAP Server (runs on sensor)
async def coap_server():
class SensorResource(Resource):
async def render_get(self, request):
payload = json.dumps({
"temperature": 22.5,
"humidity": 55,
"battery": 85
}).encode()
return Message(
payload=payload,
code=CONTENT,
content_format=ContentFormat.APPLICATION_JSON
)
root = Site()
root.add_resource(['sensor', 'data'], SensorResource())
await Context.create_server_context(root, bind=('0.0.0.0', 5683))
await asyncio.get_running_loop().create_future()
# CoAP Client
async def coap_client():
protocol = await Context.create_client_context()
request = Message(
code=GET,
uri='coap://localhost:5683/sensor/data'
)
response = await protocol.request(request).response
data = json.loads(response.payload.decode())
print(f"CoAP Response: {data}")
print(f"Response code: {response.code}")
print(f"RTT: {response.opt.max_age}")
if __name__ == "__main__":
# Run server in one terminal, client in another
asyncio.run(coap_server())
# asyncio.run(coap_client())
Expected output: CoAP client retrieves sensor data with a 4-byte header — ideal for 802.15.4 and LoRaWAN networks.
HTTP/2 for IoT Gateways
import httpx
import asyncio
async def http_iot_gateway():
async with httpx.AsyncClient(http2=True) as client:
# Batch sensor readings via POST
response = await client.post(
"https://api.iot-platform.com/v1/sensors/batch",
json={
"gateway_id": "gw-bldg-01",
"readings": [
{"sensor": "temp-01", "value": 22.5},
{"sensor": "humidity-01", "value": 55},
{"sensor": "pressure-01", "value": 1013}
]
},
timeout=30
)
print(f"HTTP/2 Status: {response.status_code}")
print(f"Response: {response.json()}")
# HTTP/2 multiplexing — 10 requests in parallel
async def batch_requests():
async with httpx.AsyncClient(http2=True) as client:
tasks = []
for i in range(10):
tasks.append(
client.get(f"https://api.iot-platform.com/v1/sensors/{i}")
)
responses = await asyncio.gather(*tasks)
for r in responses:
print(f"Sensor {r.url}: {r.status_code}")
Expected output: HTTP/2 multiplexes 10 requests over a single TCP connection, reducing latency by eliminating connection overhead per request.
Mermaid Diagram: Protocol Selection Flow
flowchart TD
A[IoT Device] --> B{Power Source?}
B -->|Battery| C{Data Frequency?}
B -->|Mains| D{Message Pattern?}
C -->|Low| E[CoAP]
C -->|High| F[MQTT QoS 0]
D -->|Pub/Sub| F
D -->|Request/Response| G[REST API]
G --> H{Constrained?}
H -->|Yes| E
H -->|No| I[HTTP/2]
style A fill:#e6f3ff
style E fill:#d4edda
style F fill:#fff3cd
style I fill:#cce5ff
Protocol Benchmark
| Metric | MQTT | CoAP | HTTP/2 |
|---|---|---|---|
| 100 msgs overhead | 200 bytes | 400 bytes | 20KB |
| Battery (AA, daily use) | 18 months | 36 months | 6 months |
| Max throughput | 10K msg/s | 2K req/s | 50K req/s |
| NAT-friendly | Yes (persistent conn) | No (UDP) | Yes |
| TLS overhead | ~5KB handshake | ~3KB (DTLS) | ~6KB |
Common Protocol Errors
1. MQTT Topic Wildcard Mismatch
Problem: sensor/+/temperature vs sensor/# — wrong wildcard.
Fix: + replaces one level, # matches all remaining levels.
2. CoAP Non-Confirmable Lost
Problem: Non-confirmable message lost without retry. Fix: Use Confirmable (CON) for important data, Non-confirmable (NON) for telemetry.
3. HTTP Polling Overhead
Problem: Device polls every 5 seconds — 17K messages/month wasted. Fix: Switch to MQTT push or HTTP/2 Server-Sent Events.
4. MQTT Retained Messages Confusion
Problem: New subscriber gets stale retained message. Fix: Set retain flag only for state (current temperature), not events (alerts).
5. DTLS Handshake Failures
Problem: CoAP over DTLS fails on memory-constrained MCUs. Fix: Use pre-shared keys (PSK) instead of certificates — 1/10th the memory.
6. Port Blocking
Problem: Corporate firewall blocks port 1883 (MQTT) or 5683 (CoAP). Fix: Use MQTT over WebSockets (port 443) or CoAP over HTTP proxy.
Practice Questions
Why does MQTT use a broker? Decouples publishers from subscribers — devices don't need to know each other's addresses.
What does QoS 2 guarantee? Exactly-once delivery using a four-part handshake — highest reliability, highest overhead.
When would you choose CoAP over MQTT? When using UDP-only networks (LoRaWAN, 802.15.4) or when device memory is under 100KB.
What is MQTT's last will and testament? A message the broker publishes if the client disconnects unexpectedly — used for device status monitoring.
How does HTTP/2 improve IoT compared to HTTP/1.1? Multiplexing, header compression (HPACK), server push, and binary framing — all reduce latency.
Challenge
Build an IoT protocol gateway that accepts MQTT messages from field devices and translates them to HTTP/2 REST calls for a cloud platform. Handle QoS mapping, retry logic, and connection pooling. Benchmark throughput with 1,000 simulated devices.
Real-World Task
You deploy 500 soil moisture sensors on a farm. Each sensor reports every 30 minutes. Battery life estimate with HTTP polling: 4 months. With CoAP NON: 24 months. Implement CoAP on the sensor firmware (ESP32) and benchmark actual power consumption with a current meter.
Mini Project: Multi-Protocol Bridge
import paho.mqtt.client as mqtt
import httpx
import json
import asyncio
class MQTTtoHTTPBridge:
def __init__(self, mqtt_broker, http_endpoint):
self.http_endpoint = http_endpoint
self.client = mqtt.Client()
self.client.on_message = self.on_mqtt_message
self.client.connect(mqtt_broker)
def on_mqtt_message(self, client, userdata, msg):
data = json.loads(msg.payload.decode())
asyncio.create_task(
self.forward_to_cloud(data)
)
async def forward_to_cloud(self, data):
async with httpx.AsyncClient() as http:
response = await http.post(
self.http_endpoint,
json=data,
timeout=5
)
print(f"Forwarded: {response.status_code}")
def start(self):
self.client.subscribe("sensors/#")
self.client.loop_forever()
This bridge translates MQTT sensor readings to cloud HTTP API calls.
Related Tutorials
- MQTT — MQTT protocol deep dive
- CoAP — CoAP for constrained devices
- IoT Cloud Platforms — Cloud ingestion endpoints
- Next: IoT Edge Computing — Processing Data at the Edge Guide
- Previous: IoT Overview — Internet of Things fundamentals
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro