---
title: "MQTT, Modbus TCP or HTTP: moving meter data to a server"
description: "Poll versus publish, what each protocol costs in bandwidth and firewall holes, how to design topics and payloads, and where each one genuinely belongs."
date: "2026-07-22"
author: "Divakar B"
source: "https://energycalchq.com/blog/mqtt-vs-modbus-tcp-vs-http"
license: "© 2026 Divakar B. Quote with attribution to https://energycalchq.com/blog/mqtt-vs-modbus-tcp-vs-http"
---

Once a gateway is reading meters reliably, the next question is how the data
gets to wherever it is going to live. Three answers dominate, and they are not
really competing — they belong at different points in the same system.

The distinction that matters is not the wire format. It is **who starts the
conversation**, because that decides your firewall rules, your bandwidth, and
what happens when the link drops.

## Poll versus publish

![Polling model against publish model, showing the direction of connections](/blog/poll-versus-publish.svg "Every architectural difference between these protocols follows from the direction of that first arrow.")

**Polling** — Modbus TCP, HTTP GET — means a server asks each device for data on
a schedule. The server needs to reach every device, which means every device
needs a reachable address and an open inbound port. Most polls return data that
has not changed.

**Publishing** — MQTT — means each device connects outward to a broker and pushes
data when it has some. Nothing needs an inbound port. Consumers subscribe to the
broker, and adding a new consumer changes nothing at the edge.

## The three options compared

| | Modbus TCP | MQTT | HTTP / REST |
|---|---|---|---|
| Model | Poll | Publish / subscribe | Poll or push |
| Overhead per reading | Very low | Low (2-byte minimum header) | High (headers, often more than the payload) |
| Direction | Inbound to device | **Outbound from device** | Either |
| Works behind NAT / mobile | Poorly | Yes | Yes, if pushing |
| Security | **None built in** | TLS, per-client credentials | TLS, tokens |
| Knows a device died | No — a timeout is ambiguous | Yes — last will and testament | No |
| Buffers on reconnect | No | With QoS 1 and persistent sessions | Only if you build it |
| Best at | The field bus, over existing Ethernet | Everything northbound | Occasional bulk transfer, integrations |

**Modbus TCP** is Modbus RTU's data model over Ethernet. It is the right choice
where structured cabling already exists and the devices are on a network you
control — it is fast, simple and every SCADA package speaks it. It has no
authentication and no encryption whatsoever, which is fine on an isolated OT
VLAN and unacceptable anywhere else. Never expose it to the internet.

**MQTT** is the default for anything leaving the site. It is designed for
unreliable links and small messages, it needs no inbound ports, and it has two
features that are worth the switch on their own:

- **Last will and testament.** The client registers a message when it connects,
  and the broker publishes it if the connection dies. Your monitoring system
  learns a gateway is gone within seconds, instead of inferring it from silence.
- **Persistent sessions with QoS 1.** The broker holds messages for a
  disconnected subscriber and delivers them on reconnect.

**HTTP** is the right answer for what it is good at: batch uploads, integrating
with somebody else's API, or a low-rate system where the operational simplicity
of "just POST some JSON" is worth the overhead. Sending one reading per second
over HTTPS means a TLS handshake and several hundred bytes of headers to carry
twenty bytes of data.

## Getting MQTT right

**Topic structure.** Hierarchical, specific to general on the left, one device
per branch:

```
site/plant1/shed2/mcc3/compressor-1/telemetry
site/plant1/shed2/mcc3/compressor-1/status
```

Rules that save pain later:

- **No wildcards or variable depth.** Subscribers use `+` and `#`, so a
  consistent depth is what makes `site/plant1/+/+/+/telemetry` work.
- **Never put data in the topic.** `.../current/47.2` cannot be subscribed to
  usefully.
- **Separate telemetry from status.** Different rates, different retention,
  different consumers.
- **Identify by role, not by hardware**, for the same reason as in the [gateway
  post](/blog/building-a-modbus-energy-gateway) — replacing a meter should not
  break a topic.

**Payload.** JSON is the sensible default: readable, debuggable, universally
supported. Include the timestamp in every message, in UTC, generated at the edge.

```json
{
  "ts": "2026-08-31T09:15:00Z",
  "kwh_import": 148237.4,
  "kw": 61.2,
  "pf": 0.91,
  "i": [88.4, 91.2, 87.9]
}
```

Send one message per device per interval with all its quantities, not one message
per quantity. Twenty meters × 40 registers as individual messages is 800 messages
a cycle, and each carries its own overhead.

**QoS.** QoS 0 for high-rate telemetry where the next reading is along shortly.
QoS 1 for anything you must not lose — energy totals, alarms, status changes.
QoS 2 is rarely worth its round trips; design your consumers to be idempotent
instead, which you need anyway.

**Retained messages** on status topics, so a newly connected dashboard
immediately knows the current state rather than waiting for the next change.

If you want a standard rather than a house convention, **Sparkplug B** defines
topic structure, payload encoding and birth/death certificates for industrial
MQTT. It is more machinery than a twenty-meter site needs, and it is a genuinely
good answer for a large estate with many vendors.

## How much data is this, really?

Worth estimating before choosing a SIM plan, because the answer surprises people
in both directions.

Twenty meters, forty quantities each, published once a minute as JSON:

```
~400 bytes per message × 20 meters × 60 min × 24 h = ~11 MB/day
                                                   = ~330 MB/month
```

Comfortable on any fixed line, and a real consideration on a metered mobile
connection. If it needs to come down, the levers in order of effect are: publish
less often (a minute is already far more often than energy analysis needs),
publish only quantities that changed, drop JSON for a binary encoding, and
enable MQTT 5 topic aliases so the topic string is not repeated on every
message.

Note what is *not* on that list: reading fewer registers from the meter. Polling
the field bus is free — it happens over your own RS485 cable. Read everything the
meter offers, using contiguous register blocks worked out with [the address
converter](/tools/modbus-address), and decide separately what to publish
northbound. Data you did not collect is gone forever; data you collected and
chose not to send is still on the gateway.

## Security, which is the actual differentiator

The protocol choice is largely a security choice, so be explicit:

- **TLS everywhere**, with certificate verification actually enabled — not
  disabled to make the demo work and then forgotten.
- **Per-device credentials**, so one compromised gateway can be revoked without
  touching the others.
- **Broker-side ACLs** limiting each client to publishing on its own topic
  branch. A gateway should not be able to publish as another site.
- **Outbound connections only.** No port forwarding, no inbound rules, ever.
- **Segment the OT network.** The gateway may talk to meters and to the broker.
  Nothing else needs to talk to the meters.

## A sensible default architecture

For almost every site under a few hundred points:

1. **Modbus RTU over RS485** from meters to a local gateway. Cheap, robust,
   works over a kilometre, no network infrastructure required.
2. **Local buffering** on the gateway, with edge timestamps.
3. **MQTT over TLS** northbound to a broker, outbound only.
4. **Subscribers** on the broker for storage, dashboards and alerting — each
   independent, each addable without touching the field.

That gives you one polling domain, at the edge, where polling is appropriate
because the devices are dumb and the bus is local. Everything upstream is
event-driven, firewall-friendly and survives an intermittent link.

The mistake to avoid is polling across the internet — a central server reaching
into twenty sites over VPNs to read Modbus TCP. It works, it is what many
systems do, and every one of those VPN tunnels is a thing that breaks at 2 a.m.
and a hole into an OT network that has no authentication behind it.
