50% OFF

ESP32-IDF Workshop

Blog/Embedded Systems

MQTT vs HTTP for IoT: Which Protocol Should Your Firmware Use?

Most IoT tutorials default to HTTP without explaining why. Here's a head-to-head comparison of MQTT and HTTP from a firmware engineer who deployed both.

| Beginner
Rajath Kumar
Rajath KumarEdge AI Engineer & Founder, Analog Data
2026-06-25·7 min read
MQTT vs HTTP for IoT: Which Protocol Should Your Firmware Use?

Every IoT tutorial eventually reaches the moment where sensor data needs to leave the device. Most reach for HTTP without a second thought. After deploying both protocols across dozens of production devices — from industrial sensor gateways to ESP32-based edge nodes — here's the honest comparison.


The Core Difference

HTTP and MQTT solve fundamentally different problems:

HTTPMQTT
ModelRequest / ResponsePublish / Subscribe
ConnectionShort-lived per requestPersistent TCP
Overhead per message~800 bytes (headers)~2 bytes minimum
DirectionClient initiates onlyBidirectional
Offline handlingNoneQoS + persistent sessions
Broker requiredNoYes

HTTP on Embedded Devices: The Real Cost

HTTP looks simple. In practice, for a device sending sensor data every 30 seconds:

c
1// Every single reading does ALL of this:
2// 1. TCP handshake         — 1 round trip
3// 2. TLS handshake (HTTPS) — 2-3 round trips
4// 3. HTTP headers          — ~600-800 bytes sent
5// 4. Response headers      — ~400-600 bytes received
6// 5. TCP teardown          — 1 round trip
7
8esp_err_t send_sensor_reading(float temperature) {
9    esp_http_client_config_t config = {
10        .url = "https://api.example.com/sensors/data",
11        .method = HTTP_METHOD_POST,
12    };
13    esp_http_client_handle_t client = esp_http_client_init(&config);
14
15    char payload[64];
16    snprintf(payload, sizeof(payload), "{\"temp\": %.2f}", temperature);
17
18    esp_http_client_set_post_field(client, payload, strlen(payload));
19    esp_err_t err = esp_http_client_perform(client);  // blocks for 200-800ms
20
21    esp_http_client_cleanup(client);
22    return err;
23}

For 2 readings per minute, that's 2,880 TLS handshakes per day. On cellular with a per-connection cost, this adds up fast. On battery, it's a device killer.


MQTT: Persistent Connection, Tiny Payloads

MQTT maintains a single TCP connection. Every publish after the initial handshake costs just a few bytes:

c
1#include "mqtt_client.h"
2
3static esp_mqtt_client_handle_t mqtt_client = NULL;
4
5void mqtt_init(void) {
6    esp_mqtt_client_config_t mqtt_cfg = {
7        .broker.address.uri = "mqtts://your-broker.example.com:8883",
8        .credentials.username = "device-001",
9        .credentials.authentication.password = "secret",
10        .session.keepalive = 60,          // ping broker every 60s to keep conn alive
11        .session.disable_clean_session = true,  // persist subscriptions across reconnects
12    };
13
14    mqtt_client = esp_mqtt_client_init(&mqtt_cfg);
15    esp_mqtt_client_register_event(mqtt_client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
16    esp_mqtt_client_start(mqtt_client);
17}
18
19void send_sensor_reading(float temperature) {
20    char payload[32];
21    snprintf(payload, sizeof(payload), "%.2f", temperature);
22
23    // After first connection: this is ~10-20 bytes on the wire
24    esp_mqtt_client_publish(
25        mqtt_client,
26        "devices/sensor-001/temperature",  // topic
27        payload,                           // payload
28        0,                                 // len 0 = use strlen
29        1,                                 // QoS 1 = at least once delivery
30        0                                  // retain = false
31    );
32}

After the initial TLS handshake, every publish is near-instant. No re-handshaking. No overhead. The broker receives the message and routes it to any subscribers — your backend, another device, a dashboard.


QoS Levels: Getting Delivery Guarantees Right

MQTT has three Quality of Service levels. Picking the wrong one is a common production mistake:

QoSGuaranteeUse When
0 — At most onceFire and forget, no ACKHigh-frequency data where loss is acceptable (e.g. live telemetry at 10Hz)
1 — At least onceBroker ACKs receipt, may duplicateSensor readings, alerts — most IoT telemetry
2 — Exactly once4-way handshake, no duplicatesFinancial transactions, command execution
c
1// QoS 0 — no guarantee, minimum overhead
2esp_mqtt_client_publish(client, "sensors/temp", "23.5", 0, 0, 0);
3
4// QoS 1 — broker confirms receipt (use this for most telemetry)
5esp_mqtt_client_publish(client, "sensors/temp", "23.5", 0, 1, 0);
6
7// QoS 2 — exactly once (expensive, use only when duplication is harmful)
8esp_mqtt_client_publish(client, "commands/executed", "reboot", 0, 2, 0);

Production rule: Use QoS 1 for all sensor telemetry. The duplicate risk is negligible and your backend can deduplicate by timestamp. QoS 2's 4-way handshake adds latency you don't need for sensor data.


Last Will and Testament: Free Device Health Monitoring

One of MQTT's most underused features — the broker publishes a message on your behalf if the device disconnects unexpectedly:

c
1esp_mqtt_client_config_t mqtt_cfg = {
2    .broker.address.uri = "mqtts://broker.example.com:8883",
3    .session.last_will = {
4        .topic = "devices/sensor-001/status",
5        .msg = "offline",
6        .msg_len = 7,
7        .qos = 1,
8        .retain = true,   // broker stores this so late subscribers see it
9    },
10};

When your device connects, publish "online" to the same topic. Any backend or dashboard subscribing to devices/+/status instantly knows which devices are live — for free, with no polling.


When to Use Which

text
1Ongoing sensor telemetry       → MQTT (QoS 1, persistent session)
2Device receives commands       → MQTT (subscribe to command topic)
3Device health monitoring       → MQTT (Last Will + retain)
4One-time boot configuration    → HTTP GET
5Device registration on first boot → HTTP POST
6Firmware OTA trigger           → MQTT (trigger) + HTTP (binary download)

Key Takeaways

  1. HTTP re-handshakes on every request — on battery or cellular, this is expensive
  2. MQTT maintains one persistent connection — publish costs ~2 bytes of overhead after connect
  3. QoS 1 is the right default for most IoT telemetry — fire-and-forget QoS 0 loses data silently
  4. Last Will gives you free device health monitoring — no extra infrastructure needed
  5. Use both — MQTT for ongoing data flow, HTTP for boot-time configuration and OTA binary downloads
Share
Live Workshop

Go from Arduino to Production Firmware

The ESP32-IDF Workshop covers ESP-IDF from scratch — tasks, queues, OTA, Wifi management, and deploying firmware that doesn't break at 3am.

Join the Workshop →

Frequently Asked Questions

Quick answers to common questions

Rajath Kumar

Written by

Rajath Kumar

Edge AI Engineer & Founder, Analog Data

I build things that run on chips and the software that talks to them. ESP32, STM32, FreeRTOS, FastAPI, TinyML — from bare-metal firmware to cloud backends to on-device inference. Based in Bengaluru. Founder of Analog Data.

More in Embedded Systems