MQTT & Internet Gateway
- Meshtastic MQTT Setup
- Building a Meshtastic Internet Gateway
- MQTT Topic Structure and Packet Format
- Preventing MQTT Message Loops
Meshtastic MQTT Setup
MQTT lets a Meshtastic node forward all mesh traffic to the internet, making your local mesh visible on the network map, bridging messages to internet clients, and enabling monitoring and logging. This is what puts your nodes on meshmap.net.
How MQTT works in Meshtastic
When MQTT is enabled on a node:
- Every mesh packet received by the node is forwarded to an MQTT broker over WiFi or TCP
- The MQTT broker stores and redistributes the messages to other subscribers
- The public Meshtastic MQTT broker (mqtt.meshtastic.org) accepts messages from anyone and shares them publicly - this is how meshmap.net gets its data
- Optionally, messages from the internet (MQTT) can be injected back into the local radio mesh
Hardware requirement: MQTT requires a WiFi-capable device (ESP32-based: Heltec V3/V4, T-Beam, etc.). nRF52840 devices (T-Echo, T-Deck, RAK4631) do not have WiFi and cannot connect to MQTT directly.
Connecting to the public Meshtastic MQTT broker
Via the Meshtastic app
- Go to Settings → Module Config → MQTT
- Enable MQTT: toggle ON
- MQTT Server Address:
mqtt.meshtastic.org - Username:
meshdev - Password:
large4cats - TLS Enabled: toggle ON (recommended)
- Map Reporting Enabled: toggle ON (to appear on meshmap.net)
- Save
Via CLI
meshtastic --set mqtt.enabled true
meshtastic --set mqtt.address mqtt.meshtastic.org
meshtastic --set mqtt.username meshdev
meshtastic --set mqtt.password large4cats
meshtastic --set mqtt.tls_enabled true
meshtastic --set mqtt.map_reporting_enabled true
Channel settings for MQTT
MQTT is enabled per channel. By default, the primary channel (channel 0) is configured to uplink to MQTT. Verify that your channel has Uplink Enabled set to ON:
meshtastic --ch-index 0 --ch-set uplink_enabled true
Important: If your channel uses a non-default PSK, messages are uploaded encrypted. The public meshmap.net can still see node positions (which are unencrypted) but cannot read message content.
Downlink: receiving messages from the internet
Downlink allows messages published to MQTT to be injected into the local radio mesh - enabling internet-connected users to send messages that appear on mesh nodes in your area:
meshtastic --ch-index 0 --ch-set downlink_enabled true
Security note: Only enable downlink on channels with PSK authentication if you want to control who can inject messages into your local mesh. The public LongFast channel has no authentication - anyone on the public MQTT broker can inject messages into your mesh if downlink is enabled on the default channel.
Running a private MQTT broker
For a community or organizational network, run your own Mosquitto broker instead of using the public one:
# Install Mosquitto
sudo apt install mosquitto mosquitto-clients
# Basic config: /etc/mosquitto/mosquitto.conf
listener 1883 localhost # Local only (use nginx/TLS for external)
listener 8883 # TLS port for internet clients
cafile /path/to/ca.crt
certfile /path/to/server.crt
keyfile /path/to/server.key
allow_anonymous false
password_file /etc/mosquitto/passwd
Point your Meshtastic nodes to your broker's address instead of mqtt.meshtastic.org.
MQTT topic structure
Meshtastic publishes to topics of the form:
msh/{region}/{channel_name}/2/json/{packet_type}/{node_id}
Examples:
msh/US/LongFast/2/json/nodeinfo/!abcd1234
msh/US/LongFast/2/json/position/!abcd1234
msh/US/LongFast/2/json/text/!abcd1234
Subscribe to msh/US/# to receive all US region traffic. The payload is JSON-encoded packet data.
Building a Meshtastic Internet Gateway
A Meshtastic internet gateway bridges local LoRa radio traffic to the internet and can serve as a powerful community infrastructure node. This guide covers setting up a dedicated gateway on a Raspberry Pi.
Gateway hardware options
| Option | Hardware | Pros | Cons |
|---|---|---|---|
| ESP32 node (simplest) | Heltec V3/V4, T-Beam | Single device, no Pi needed, compact | Single-channel, limited processing |
| Pi + LoRa hat | Raspberry Pi 4 + RAK2287/RAK5146 | Multi-channel gateway possible, full Linux environment | More complex setup |
| Pi + USB LoRa node | Raspberry Pi + RAK4631 USB | Easy setup, use standard Meshtastic firmware | Single channel only |
Option 1: Dedicated ESP32 gateway node
The simplest approach: configure a Heltec V3/V4 or T-Beam as a dedicated gateway. This node doesn't need to be a router/repeater - its primary job is bridging radio and MQTT.
- Flash with Meshtastic firmware (standard)
- Connect to your home or community WiFi:
meshtastic --set network.wifi_ssid "YourSSID" --set network.wifi_psk "YourPassword" - Configure MQTT as described in the MQTT Setup page
- Set role:
meshtastic --set device.role ROUTER(so it actively forwards radio packets) - Mount at a good location with LoRa antenna and reliable WiFi
Option 2: Raspberry Pi + USB LoRa node
Connect an nRF52840-based device (RAK4631, T-Echo) via USB to a Raspberry Pi. The Pi handles internet connectivity while the LoRa node handles radio.
Setup the LoRa node
# Flash with Meshtastic firmware
# Set role to ROUTER
meshtastic --port /dev/ttyUSB0 --set device.role ROUTER
Install meshtasticd (Meshtastic daemon)
pip3 install meshtastic
pip3 install meshtasticd # If available for your platform
Configure MQTT bridging via Python
import meshtastic
import meshtastic.serial_interface
import paho.mqtt.client as mqtt
import json
# Connect to the LoRa node
iface = meshtastic.serial_interface.SerialInterface("/dev/ttyUSB0")
# Connect to MQTT broker
mq = mqtt.Client()
mq.username_pw_set("meshdev", "large4cats")
mq.tls_set()
mq.connect("mqtt.meshtastic.org", 8883)
# Forward all received packets to MQTT
def on_receive(packet, interface):
topic = f"msh/US/LongFast/2/json/{packet.get('decoded', {}).get('portnum', 'unknown')}/{packet.get('from', '?')}"
mq.publish(topic, json.dumps(packet))
iface.on_receive = on_receive
mq.loop_forever()
Monitoring your gateway
A healthy gateway should be publishing to MQTT continuously. Monitor with:
# Subscribe to your node's traffic (replace !abcd1234 with your node ID)
mosquitto_sub -h mqtt.meshtastic.org -p 8883 -t "msh/US/#" -u meshdev -P large4cats --tls-use-os-certs | grep abcd1234
You should see JSON packets appearing whenever your node hears a packet on the mesh. If the stream is silent for more than a few minutes in an active network, your WiFi or MQTT connection may have dropped.
Adding your gateway to the community map
With map_reporting_enabled true and a valid GPS position set on your node, your gateway will automatically appear on meshmap.net within a few minutes of first connecting. Verify at meshmap.net by searching for your node name.
MQTT Topic Structure and Packet Format
Understanding Meshtastic's MQTT topic structure and packet encoding lets you build integrations, parse data, and troubleshoot gateway connectivity issues.
Topic Structure
Meshtastic publishes to MQTT topics using this hierarchy:
msh/{region}/{protocol_version}/{channel_type}/{channel_name}/{node_id}
Examples:
msh/US/2/e/LongFast/!ab12cd34 # Encrypted packet from node ab12cd34
msh/US/2/json/LongFast/!ab12cd34 # JSON-decoded packet (if node has JSON enabled)
Where:
msh/ - Meshtastic prefix
US/ - Region code (US, EU, etc.)
2/ - Protocol version
e/ - Encrypted (default)
json/ - JSON decoded (optional, if JSON output enabled on gateway)
LongFast/ - Channel name
!ab12cd34 - Source node ID
Subscribing to All Traffic
# Subscribe to all Meshtastic packets from all US nodes:
mosquitto_sub -h mqtt.meshtastic.org -u meshdev -P large4cats -t "msh/US/2/e/#" -v
# Subscribe to a specific channel only:
mosquitto_sub -h mqtt.meshtastic.org -t "msh/US/2/e/CommunityMesh/#" -v
# Subscribe to JSON-decoded packets (if gateway has JSON enabled):
mosquitto_sub -h localhost -t "msh/US/2/json/#" -v
Packet Format
Each MQTT message payload is a protobuf-encoded ServiceEnvelope containing:
- packet - The MeshPacket (encrypted payload + routing info)
- channel_id - Channel name (e.g., "LongFast")
- gateway_id - Node ID of the gateway that published to MQTT
Enabling JSON Output on a Gateway Node
# Enable JSON output in addition to (not instead of) protobuf:
meshtastic --set mqtt.json_enabled true
# JSON packets are published to: msh/US/2/json/{channel}/{node_id}
# JSON payload example:
{
"from": 2881537332,
"to": 4294967295,
"channel": 0,
"id": 123456789,
"rx_time": 1712000000,
"hop_limit": 3,
"payload": {
"text": "Hello mesh!"
}
}
Decoding Protobuf Packets in Python
import paho.mqtt.client as mqtt
from meshtastic.mesh_pb2 import ServiceEnvelope
from meshtastic.portnums_pb2 import PortNum
import base64
def on_message(client, userdata, msg):
try:
se = ServiceEnvelope()
se.ParseFromString(msg.payload)
packet = se.packet
print(f"From: !{packet.from_:08x}")
print(f"To: !{packet.to:08x}")
print(f"Channel: {se.channel_id}")
# Decrypt and decode based on portnum for full payload
except Exception as e:
print(f"Parse error: {e}")
client = mqtt.Client()
client.username_pw_set("meshdev", "large4cats")
client.on_message = on_message
client.connect("mqtt.meshtastic.org", 1883)
client.subscribe("msh/US/2/e/#")
client.loop_forever()
Preventing MQTT Message Loops
A common misconfiguration in networks with MQTT gateways is the "MQTT loop" - packets sent over LoRa get forwarded to MQTT, which then re-injects them back into the LoRa network, causing each message to be transmitted multiple times and rapidly increasing channel utilization.
How MQTT Loops Happen
- Node A sends a message over LoRa
- Gateway node G receives the LoRa packet and publishes it to MQTT
- MQTT broker delivers the packet to Gateway G's downlink subscription
- Gateway G injects the packet back into the LoRa network
- Node A receives its own message again as if from the MQTT cloud
- This can loop indefinitely if not properly configured
Prevention: The ignore_mqtt Setting
The most important anti-loop protection is the lora.ignore_mqtt flag. When enabled, a node will not re-broadcast packets that originated from MQTT (identified by the "via MQTT" flag in the packet header):
# Enable on ALL infrastructure nodes (routers, repeaters):
meshtastic --set lora.ignore_mqtt true
# Verify:
meshtastic --get lora.ignore_mqtt
Critical: Set this on every ROUTER, ROUTER_CLIENT, and REPEATER role node in your network. Leave it as false only on end-client nodes that may need to receive messages downlinked from MQTT.
Gateway Configuration Best Practices
# Configure the gateway node correctly:
# 1. Enable MQTT publishing (uplink):
meshtastic --ch-index 0 --ch-set uplink_enabled true
# 2. Enable MQTT subscribing (downlink) ONLY if needed:
# Only enable downlink if you want to receive messages sent to MQTT
# from the global mesh or other external systems.
# If you only want to publish (monitoring), keep downlink disabled:
meshtastic --ch-index 0 --ch-set downlink_enabled false
# 3. Enable ignore_mqtt on the gateway node itself:
meshtastic --set lora.ignore_mqtt true
Detecting a Loop in Progress
Signs that a loop is occurring:
- Channel utilization (CU) rapidly increasing after enabling MQTT
- Messages appearing in the chat multiple times
- The same packet ID appearing in MQTT subscription twice or more within a few seconds
- Nodes reporting high packet counts in stats
# Check channel utilization (should be under 15% in normal operation):
meshtastic --get channel_utilization
# Monitor for duplicate packet IDs via MQTT:
mosquitto_sub -t "msh/US/2/e/#" -v | grep -E "packet.id"
# Watch for repeating IDs within 30 seconds