Networking & Connectivity

IoT Communication Protocols

Compare MQTT, HTTP, CoAP, WebSockets, and AMQP — understand when to use each protocol for sensor telemetry, commands, and real-time dashboards.

IoT Communication Protocols

Choosing the right communication protocol affects bandwidth usage, battery life, latency, and reliability. IoT protocols are optimized for constrained devices and unreliable networks — quite different from typical web protocols.


Why Standard Web Protocols Don't Always Work for IoT

HTTP Request/Response overhead:
┌──────────────────────────────────────┐
│ GET /temperature HTTP/1.1            │  Headers alone are
│ Host: api.example.com                │  300–800 bytes per request
│ Accept: application/json             │
│ Authorization: Bearer eyJhbGc...    │
│ User-Agent: Python/3.11 requests/2.28│
└──────────────────────────────────────┘

MQTT Publish overhead:
┌──────────────────────────┐
│ PUBLISH header: 2 bytes  │  Fixed header = 2 bytes
│ Topic: sensors/temp      │  Total packet < 30 bytes
│ Payload: 24.5            │  for a temperature reading
└──────────────────────────┘

For a sensor sending data every 5 minutes on a 2G connection with 50 KB/month data cap, that difference is the gap between a working system and a runaway data bill.


MQTT — Message Queuing Telemetry Transport

MQTT is the dominant IoT protocol. It uses a publish/subscribe model with a central broker.

Device (Publisher)         Broker                  Dashboard (Subscriber)
        │                    │                            │
        │── PUBLISH ────────►│                            │
        │  topic: home/temp  │◄── SUBSCRIBE ─────────────│
        │  payload: 24.5     │   topic: home/temp         │
        │                    │── FORWARD ────────────────►│
        │                    │   home/temp: 24.5          │
python
# Python (PC/Raspberry Pi) — MQTT publisher
import paho.mqtt.client as mqtt
import json
import time
import random

BROKER = "broker.hivemq.com"  # Free public test broker
PORT   = 1883
TOPIC  = "bigxstar/sensors/env"

client = mqtt.Client(client_id="sensor-node-01")

def on_connect(client, userdata, flags, rc):
    codes = {0: "Connected", 1: "Wrong protocol", 4: "Bad credentials"}
    print(f"Broker: {codes.get(rc, f'Error {rc}')}")

client.on_connect = on_connect
client.connect(BROKER, PORT, keepalive=60)
client.loop_start()

try:
    while True:
        payload = json.dumps({
            "device_id": "sensor-01",
            "temperature": round(20 + random.uniform(-2, 5), 2),
            "humidity":    round(55 + random.uniform(-5, 10), 2),
            "timestamp":   int(time.time())
        })
        result = client.publish(TOPIC, payload, qos=1)
        print(f"Published [{result.rc}]: {payload}")
        time.sleep(10)
except KeyboardInterrupt:
    client.loop_stop()
    client.disconnect()
python
# Python — MQTT subscriber (receiving data)
import paho.mqtt.client as mqtt
import json

BROKER = "broker.hivemq.com"
TOPIC  = "bigxstar/sensors/#"  # # wildcard: all sub-topics

def on_connect(client, userdata, flags, rc):
    print("Connected to broker")
    client.subscribe(TOPIC, qos=1)

def on_message(client, userdata, msg):
    try:
        data = json.loads(msg.payload.decode())
        print(f"[{msg.topic}]")
        print(f"  Device:      {data['device_id']}")
        print(f"  Temperature: {data['temperature']} °C")
        print(f"  Humidity:    {data['humidity']} %")
        print()
    except (json.JSONDecodeError, KeyError) as e:
        print(f"Parse error: {e}")

client = mqtt.Client(client_id="dashboard-listener")
client.on_connect = on_connect
client.on_message = on_message

client.connect(BROKER, 1883, 60)
client.loop_forever()  # Blocks, handles reconnection automatically

MQTT QoS Levels

QoSNameGuaranteeMessagesUse When
0At most onceNo guarantee — may be lost1High-frequency, loss-tolerant (sensor stream)
1At least onceDelivered, may be duplicated1+Commands, alerts (handle duplicates)
2Exactly onceDelivered exactly once2+Billing, critical state changes
python
# QoS in practice
client.publish(topic, payload, qos=0)  # Fire and forget
client.publish(topic, payload, qos=1)  # At least once (most common)
client.publish(topic, payload, qos=2)  # Exactly once (2× round trip)

MQTT Topic Design

Topics are hierarchical strings separated by /. Good topic design makes your system scalable.

Recommended pattern:
  {org}/{location}/{device_type}/{device_id}/{measurement}

Examples:
  factory/building-a/temperature/sensor-42/celsius
  home/bedroom/humidity/node-01/percent
  fleet/truck-007/gps/location

Wildcards (subscribe only):
  + = single level:   home/+/humidity  (any room)
  # = multi-level:    home/#           (everything under home)

Avoid:
  /sensor/1  (leading slash wastes bytes)
  data       (too generic, won't scale)
  home/temp/24.5 (embedding value in topic — use payload instead)

HTTP/REST — Simple but Heavy

HTTP is the protocol of the web. It's great for non-real-time reporting to cloud APIs.

python
# MicroPython on ESP32 — HTTP POST to REST API
import urequests
import ujson
import time

API_URL = "https://api.example.com/v1/readings"
API_KEY = "your-api-key-here"

def send_reading(temperature, humidity):
    headers = {
        "Content-Type": "application/json",
        "X-API-Key": API_KEY
    }
    body = ujson.dumps({
        "sensor_id": "node-01",
        "temperature": temperature,
        "humidity": humidity,
        "timestamp": time.time()
    })

    try:
        r = urequests.post(API_URL, data=body, headers=headers)
        if r.status_code == 201:
            print("Sent successfully")
        else:
            print(f"Error: HTTP {r.status_code}")
        r.close()
    except Exception as e:
        print(f"Request failed: {e}")

send_reading(24.5, 61)

WebSockets — Real-Time Bidirectional

WebSockets maintain a persistent TCP connection for real-time, two-way communication — ideal for live dashboards.

python
# Python server — WebSocket IoT data relay
import asyncio
import websockets
import json

connected_clients = set()
latest_readings = {}

async def relay(websocket):
    connected_clients.add(websocket)
    try:
        # Send current readings immediately on connect
        if latest_readings:
            await websocket.send(json.dumps(latest_readings))

        # Keep connection alive
        async for message in websocket:
            data = json.loads(message)
            if data.get("type") == "sensor_data":
                latest_readings.update(data)
                # Broadcast to all connected dashboards
                if connected_clients:
                    await asyncio.gather(*[
                        c.send(json.dumps(data))
                        for c in connected_clients
                        if c != websocket
                    ])
    finally:
        connected_clients.discard(websocket)

async def main():
    async with websockets.serve(relay, "0.0.0.0", 8765):
        print("WebSocket server running on ws://0.0.0.0:8765")
        await asyncio.Future()  # Run forever

asyncio.run(main())

CoAP — Constrained Application Protocol

CoAP is like HTTP but built for constrained devices. It runs over UDP (not TCP) and uses 4-byte headers.

FeatureHTTPCoAP
TransportTCPUDP
Header size300–800 B4 B
MethodsGET/POST/PUT/DELETEGET/POST/PUT/DELETE
ReliabilityBuilt-in (TCP)Confirmable messages
MulticastNoYes
Use caseWeb, REST APIsLow-power devices, M2M

Protocol Decision Matrix

python
# How to choose your IoT protocol

def choose_protocol(needs):
    """
    needs = dict of requirements
    """
    if needs.get("real_time_dashboard"):
        return "WebSockets"

    if needs.get("device_very_constrained"):
        if needs.get("unreliable_network"):
            return "CoAP (UDP-based, handles packet loss)"
        return "MQTT QoS 0 or 1"

    if needs.get("simple_cloud_api"):
        return "HTTP/REST"

    if needs.get("fan_out"):  # One message to many devices
        return "MQTT (pub/sub naturally handles this)"

    if needs.get("enterprise_reliability"):
        return "AMQP (RabbitMQ)"

    # Default: MQTT is almost always a good choice
    return "MQTT"

print(choose_protocol({"fan_out": True, "device_very_constrained": True}))
# MQTT

Protocol Comparison Summary

ProtocolTransportOverheadQoSReal-timeBest For
MQTTTCPVery low0/1/2NearSensor telemetry, commands
HTTPTCPHighNoneNoCloud API reporting
CoAPUDPVery lowConfirmableNearUltra-constrained devices
WebSocketsTCPLowNoneYesLive dashboards, bidirectional
AMQPTCPMediumFullNearEnterprise, financial IoT

Summary

  • MQTT is the de facto standard for IoT — lightweight, broker-based, pub/sub model
  • QoS 1 is the sweet spot for most sensor data: guaranteed delivery with low overhead
  • HTTP is fine for non-real-time, low-frequency cloud API calls
  • WebSockets are the right choice when you need a live dashboard with instant updates
  • CoAP is MQTT's sibling for devices where even TCP is too heavy
  • Design MQTT topics hierarchically — avoid flat structures that won't scale

Next, we'll dive deep into wireless technologies — WiFi, BLE, Zigbee, LoRa, and cellular.