What is an MQTT Broker? The Traffic Cop of IoT
An MQTT broker is a centralized server that receives, filters, and routes messages between IoT devices based on a publish/subscribe model. If you are asking what is an mqtt broker in practical terms, think of it as a high-speed post office sorting facility. Devices (clients) do not talk directly to each other. Instead, a sensor publishes a payload to a specific "topic" (like home/livingroom/temp), and the broker instantly pushes that payload to any device subscribed to that exact topic.
This architecture completely decouples publishers and subscribers in both space and time. The sensor doesn't need to know the IP address of the dashboard reading its data, and the dashboard doesn't need to be online when the sensor publishes. The broker handles the queueing, routing, and quality of service (QoS) guarantees. Popular open-source and enterprise brokers include Eclipse Mosquitto, HiveMQ, and EMQX.
Network & Physical Layer Mechanics
Unlike hardware buses like I2C or CAN, MQTT is an OASIS-standardized application-layer protocol (OSI Layer 7) that runs over TCP/IP. It doesn't have a physical "bus" of its own, but it relies entirely on the physical transport layer beneath it. When designing an IoT network, you must map MQTT's logical requirements to physical realities.
| Parameter | Standard LAN/Wi-Fi (MQTT) | Industrial Edge (MQTT-SN via RS-485) |
|---|---|---|
| Physical Medium | Cat5e/Cat6 (Ethernet), 802.11ax (Wi-Fi) | Twisted pair shielded cable (RS-485 to Gateway) |
| Speed / Bandwidth | 100M-1Gbps physical; ~10k msgs/sec broker throughput | 9600 to 115200 baud (serial); ~50 msgs/sec |
| Addressing / Routing | IP:Port (Default 1883 or 8883 for TLS) + Client ID | Node ID (1-byte) mapped to gateway MAC/IP |
| Max Distance | 100m (Ethernet copper); Global via Internet routing | 1200m (RS-485 run to the MQTT gateway) |
Physical Wiring and Pull-Up Requirements
Because standard MQTT runs over TCP/IP, the physical layer (Ethernet magnetics or Wi-Fi RF) requires no external pull-up resistors. However, in industrial or agricultural setups, running Wi-Fi to every sensor is impossible. Here, we use MQTT-SN (Sensor Networks) bridged to standard MQTT via an RS-485 to Ethernet gateway (like the USR-N510).
If you are wiring legacy serial sensors to an MQTT gateway via RS-485, the physical layer requires a 120Ω termination resistor at both ends of the daisy chain. Furthermore, you must install 470Ω pull-up (to VCC) and 470Ω pull-down (to GND) bias resistors on the A and B lines. Without these pull-ups, the RS-485 lines will float when no node is transmitting, causing the gateway to read garbage data and fail to forward MQTT payloads.
Minimal Working Exchange: ESP32 to Mosquitto
Let's look at a minimal working exchange. We will wire an ESP32-WROOM-32 to a BME280 sensor, read the data, and publish it to a local Mosquitto broker. For the physical I2C bus connecting the sensor to the ESP32, 4.7kΩ pull-up resistors on the SDA and SCL lines are mandatory if your breakout board doesn't include them.
Wiring Map:
- ESP32 GPIO 21 (SDA) → BME280 SDA (with 4.7kΩ pull-up to 3.3V)
- ESP32 GPIO 22 (SCL) → BME280 SCL (with 4.7kΩ pull-up to 3.3V)
- ESP32 3.3V → BME280 VIN
- ESP32 GND → BME280 GND
#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <Adafruit_BME280.h>
// Network & Broker Config
const char* ssid = "YourNetworkSSID";
const char* password = "YourNetworkPassword";
const char* mqtt_server = "192.168.1.50"; // Local Mosquitto IP
const int mqtt_port = 1883;
// Hardware Config
#define I2C_SDA 21
#define I2C_SCL 22
WiFiClient espClient;
PubSubClient client(espClient);
Adafruit_BME280 bme;
// Generate unique client ID to prevent clashes
String clientId = "ESP32-" + String((uint32_t)ESP.getEfuseMac(), HEX);
void setup_wifi() {
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) { delay(500); }
}
void reconnect() {
while (!client.connected()) {
// The second parameter is the Last Will and Testament (LWT)
if (client.connect(clientId.c_str(), "status/offline", 1, true, "offline")) {
client.publish("status/online", clientId.c_str());
} else {
delay(5000); // Wait 5s before retrying
}
}
}
void setup() {
Serial.begin(115200);
Wire.begin(I2C_SDA, I2C_SCL);
if (!bme.begin(0x76, &Wire)) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1); // Halt execution
}
setup_wifi();
client.setServer(mqtt_server, mqtt_port);
}
void loop() {
if (!client.connected()) { reconnect(); }
client.loop();
float temp = bme.readTemperature();
String payload = "{\"temp\":" + String(temp) + "}";
// Publish to topic with QoS 1 (Acknowledged delivery)
client.publish("home/lab/temperature", payload.c_str(), true);
delay(10000); // Publish every 10 seconds
}
Protocol Selection: When to Use MQTT vs HTTP vs CoAP
Choosing the right protocol depends entirely on your device count, network reliability, and payload size. MQTT is not a silver bullet; it is optimized for persistent, low-bandwidth telemetry.
| Criteria | MQTT (TCP) | HTTP/REST (TCP) | CoAP (UDP) |
|---|---|---|---|
| Best For | High device count, continuous telemetry, real-time push | Low device count, large payloads, infrequent polling | Highly constrained nodes, lossy/sleepy cellular networks |
| Connection Model | Persistent (Long-lived TCP socket) | Transient (Request/Response) | Connectionless (UDP datagrams) |
| Overhead | ~2 bytes header per message | ~800+ bytes (HTTP headers) | ~4 bytes header |
| Firewall Traversal | Poor (Requires open inbound port 1883/8883) | Excellent (Uses standard port 80/443) | Poor (Requires open UDP ports) |
Debugging the "Bus": Sniffing and Classic Failures
When an IoT network fails, the issue is rarely the broker itself; it's almost always a physical layer fault, a network routing block, or a logical client error. Here is how to sniff the traffic and fix the classic failures.
How to Sniff and Debug
- Wireshark: Capture traffic on your router or gateway and apply the display filter
tcp.port == 1883. Wireshark natively decodes MQTT v5.0 packets, allowing you to inspect CONNECT, PUBLISH, and SUBSCRIBE headers in plain text. - MQTT Explorer: A free GUI tool that connects to your broker and visualizes the entire topic tree and payload history in real-time. Essential for verifying if a payload is actually reaching the broker.
- Mosquitto Verbose Mode: Run your local broker with the
-vflag to print every single packet received and routed to the system console.
The Classic Failures
- The Client ID Clash (Address Clash): In MQTT, the "address" is the Client ID. If you flash 50 ESP32s with the exact same firmware and hardcode
client.connect("sensor_node"), the broker will accept the first connection. When the second node connects with the same ID, the broker assumes the first node is a stale zombie and disconnects it. The first node reconnects, kicking the second. This creates an infinite connect/disconnect loop that can crash low-end brokers. Fix: Always generate Client IDs dynamically using the chip's MAC address or silicon ID. - Missing Pull-Ups & Baud Mismatch (Gateway Layer): If you are bridging serial sensors to MQTT via an RS-485 gateway, a missing 470Ω bias resistor (pull-up) or a baud rate mismatch (e.g., gateway set to 9600, sensor transmitting at 115200) will cause the gateway to read corrupted bytes. The gateway will silently drop the Modbus/serial poll, and the MQTT payload will never be generated. The broker isn't dropping your message; the physical layer never handed it to the network layer.
- The QoS 0 Blackhole: If a device publishes with QoS 0 (Fire and Forget) to a topic that currently has zero active subscribers, the broker immediately deletes the message. If your dashboard boots up 5 seconds after the sensor publishes, the data is gone. Fix: Use QoS 1 for critical telemetry, or configure your broker to retain the last known message on specific topics using the
retainflag.
Frequently Asked Questions
What is an MQTT broker vs an MQTT client?
The broker is the central server (like Mosquitto or HiveMQ) that stores subscriptions and routes messages. The client is the endpoint device (like an ESP32, Raspberry Pi, or smartphone app) that publishes data to the broker or subscribes to receive data. A single device can act as both a publisher and a subscriber simultaneously, but it always communicates exclusively with the broker, never directly with another client.
Can an MQTT broker work without the internet?
Yes. MQTT relies on TCP/IP, which functions perfectly on a localized Local Area Network (LAN). You can run a Mosquitto broker on a Raspberry Pi connected to an offline network switch, and your ESP32 sensors can publish to it via local Wi-Fi. This is the standard architecture for off-grid solar monitoring, remote agricultural sites, and secure smart home setups that require zero cloud dependency.
How much RAM does a local MQTT broker need?
For a standard hobbyist or smart home setup (under 100 devices, sending telemetry every few seconds), Eclipse Mosquitto is incredibly lightweight and will run comfortably on a Raspberry Pi Zero W with 512MB of RAM, consuming less than 15MB of memory. However, if you are building an enterprise deployment handling 100,000+ concurrent connections with high-frequency QoS 2 message queueing, you will need a dedicated server with 16GB+ of RAM and NVMe storage for persistent message logging.
Why is my MQTT broker dropping connections randomly?
Random drops are usually caused by the TCP Keep-Alive mechanism. When an ESP32 connects, it negotiates a "Keep-Alive" interval (e.g., 60 seconds). If the broker does not receive any packet (data, ping, or subscribe) from that client within 1.5x the keep-alive window, it assumes the device died and severs the TCP socket. If your device goes into deep sleep or experiences Wi-Fi dead zones without sending a PINGREQ, the broker will drop it. Ensure your firmware handles reconnection logic gracefully and matches the keep-alive timeout to your device's actual transmission schedule.






