# Gateway Setup Guides

Step-by-step setup guides for Raspberry Pi MQTT gateways and MeshCore Linux room servers.

# Raspberry Pi MQTT Gateway Setup

## What This Achieves

The Meshtastic node acts as the MQTT gateway, bridging LoRa packets to an MQTT broker (it uplinks/downlinks LoRa&lt;-&gt;MQTT using its own network connection or the connected client's). Mosquitto running on the Raspberry Pi is just the broker: it provides a persistent, always-on broker and host so you do not need a phone or laptop running the [Meshtastic app](https://wiki.meshamerica.com/books/hardware-guide/page/meshtastic-app). The node must have `mqtt.enabled` set and point at the broker for any bridging to happen - running Mosquitto alone does not bridge the mesh. Once configured it forwards mesh packets to MQTT (locally and/or to a cloud broker), enables remote monitoring of all nodes on your mesh, and allows the node to relay messages between the mesh and internet-connected services.

## Hardware Requirements

<table id="bkmrk-componentnotes-raspb"> <thead> <tr><th>Component</th><th>Notes</th></tr> </thead> <tbody> <tr><td>Raspberry Pi 3B+, 4, or Zero 2W</td><td>3B+ or 4 for comfort; Zero 2W for power-constrained installs. All run Pi OS Lite adequately.</td></tr> <tr><td>MicroSD card (16 GB+)</td><td>Class 10 / A1 rated. Use a quality brand - SD card failures are the #1 Pi reliability issue.</td></tr> <tr><td>Meshtastic USB node</td><td>T-Beam, Heltec V3, RAK WisBlock, or any supported device that presents a USB serial port (native CDC-ACM, or via a CP210x/CH340 USB-UART bridge that appears as a ttyUSB device). See [meshtastic.org/docs/hardware](https://meshtastic.org/docs/hardware/devices/) for supported devices.</td></tr> <tr><td>USB cable</td><td>Data-capable USB-A to USB-C (or micro, depending on node).</td></tr> <tr><td>Case and power supply</td><td>Official Pi PSU or PoE HAT for rooftop deployments.</td></tr> </tbody></table>

## Software Setup

### Step 1 - Flash the OS

Use **Raspberry Pi Imager** to flash **Raspberry Pi OS Lite (64-bit)** to the SD card. In the imager's Advanced Options, pre-configure:

- Hostname (e.g. `mesh-gw-01`)
- Username and password (since Pi OS Bookworm there is no default `pi` user - the first user is created here; note the name you choose, you will need it for the systemd unit below)
- SSH enabled with your public key
- WiFi credentials (or leave blank if using Ethernet)

### Step 2 - Install dependencies

```
sudo apt update && sudo apt upgrade -y
sudo apt install -y mosquitto mosquitto-clients python3-pip
pip3 install meshtastic
```

### Step 3 - Configure Mosquitto

Edit `/etc/mosquitto/mosquitto.conf` (or create a file in `/etc/mosquitto/conf.d/`). **Mosquitto 2.0+ (the version shipped by apt) defaults `allow_anonymous` to false and, with no listener defined, binds to localhost only** - a bare install rejects LAN clients until you add a listener and either allow anonymous access or a password file:

```
# Allow anonymous local connections (safe for LAN-only installs)
listener 1883
allow_anonymous true

# For remote access, use authentication instead:
# listener 1883 0.0.0.0
# allow_anonymous false
# password_file /etc/mosquitto/passwd
```

```
sudo systemctl enable mosquitto
sudo systemctl start mosquitto
```

### Step 4 - Configure the Meshtastic node

Connect to the node via the Meshtastic app or CLI and set:

- **MQTT server / address**: `localhost` (if running on the same Pi) or the Pi's LAN IP from another device.
- **MQTT port**: 1883
- **Uplink enabled**: Yes (per-channel; uplink defaults to off, so enable it on each channel you want to bridge)
- **Downlink enabled**: Yes (to receive messages from MQTT back to the mesh)
- **Encryption**: `mqtt.encryption_enabled` is a separate toggle for whether protobuf payloads are sent encrypted. Set `mqtt.encryption_enabled = false` to send unencrypted protobuf. For plaintext JSON, set `mqtt.json_enabled = true` - JSON packets are always unencrypted, regardless of `encryption_enabled`. (JSON is not supported on nRF52 boards such as RAK WisBlock.)

Via CLI:

```
meshtastic --set mqtt.address localhost --set mqtt.enabled true --set mqtt.json_enabled true
# uplink is per-channel and defaults to off - enable it on the channel you want bridged:
meshtastic --ch-index 0 --ch-set uplink_enabled true
```

### Step 5 - Verify packets are flowing

```
mosquitto_sub -h localhost -t 'msh/#' -v
```

With JSON enabled (`mqtt.json_enabled true`), text, position, and telemetry packets appear as readable JSON under `msh/REGION/2/json/...`. Without JSON, the payload under `msh/REGION/2/e/...` is raw protobuf (encrypted or unencrypted) and `mosquitto_sub` displays it as binary, not readable separate messages. If `mosquitto_sub` returns immediately or hangs with nothing at all, confirm `allow_anonymous true` is actually applied - Mosquitto 2.0 refuses anonymous connections by default; add `-u`/`-P` if you set a password.

## Remote Access Options

- **LAN only**: Listen on `127.0.0.1` or LAN IP. Accessible only within your local network - simplest and most secure.
- **Internet-exposed with auth**: Set `listener 1883 0.0.0.0` with a password file. Open port 1883 in your router/firewall only if you need external access. Consider using TLS (Mosquitto supports it natively).
- **Cloud MQTT broker**: Point your nodes at EMQX Cloud, HiveMQ Cloud, or a self-hosted Mosquitto VPS. Multiple Pi gateways in different locations all publish to the same broker - gives you a unified view of all gateways from anywhere.

## systemd Service for the Meshtastic Connection

If you run a Python script to bridge or monitor the mesh, create `/etc/systemd/system/mesh-bridge.service`. **Replace `youruser` below with the actual username you created when imaging** - since Pi OS Bookworm there is no default `pi` account, and the unit will fail to start if the user does not exist:

```
[Unit]
Description=Meshtastic mesh bridge
After=network.target mosquitto.service

[Service]
User=youruser
ExecStart=/usr/bin/python3 /home/youruser/mesh_bridge.py
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target
```

```
sudo systemctl enable mesh-bridge
sudo systemctl start mesh-bridge
```

## Node-RED on the Same Pi

Install Node-RED for visual flow-based packet processing with zero additional cloud dependency:

```
bash <(curl -sL https://github.com/node-red/linux-installers/releases/latest/download/update-nodejs-and-nodered-deb)
```

Use the MQTT-in node subscribed to `msh/#` to receive all mesh packets, then add function nodes to parse JSON, filter by type, and push to dashboards, databases, or notification services. The Node-RED UI dashboard module provides a web-accessible map and message log without any external services.

## Power Budget

<table id="bkmrk-boardidle-powernotes"> <thead> <tr><th>Board</th><th>Idle / light-load power</th><th>Notes</th></tr> </thead> <tbody> <tr><td>Raspberry Pi 4 (2 GB)</td><td>~3.4 W</td><td>Idle/light-load figure, not peak. PoE HAT adds ~1 W; suitable for rooftop enclosure with PoE switch</td></tr> <tr><td>Raspberry Pi 3B+</td><td>~2.9 W</td><td>Idle/light-load figure. Good balance of capability and power</td></tr> <tr><td>Raspberry Pi Zero 2W</td><td>~0.9 W</td><td>Idle/light-load figure. Best for solar/battery; limited to single USB device, requires USB OTG adapter</td></tr> </tbody></table>

Add ~0.5 - 1 W (average) for the connected Meshtastic node; TX bursts at +22 dBm draw more momentarily. The figures above are idle/light-load estimates, not peak. A 12 V/7 Ah SLA battery is ~84 Wh nominal, but SLA chemistry should only be discharged to ~50% depth to avoid damage (~42 Wh usable), and a 12 V-&gt;5 V buck converter loses ~10-15%. Against a Zero 2W + node load of roughly 1.5-1.9 W, expect about **20-25 hours** of runtime, not 40+. Reaching 40+ hours would require deep discharge that shortens SLA lifespan.

# Setting Up a Meshtastic MQTT-to-Internet Gateway

An MQTT gateway connects your Meshtastic mesh to the internet, enabling message delivery to non-LoRa clients, integration with home automation, and connection to the global Meshtastic MQTT network.

## What the MQTT Gateway Does

A Meshtastic node in "MQTT gateway" mode:

- Receives LoRa packets on channels that have MQTT uplink enabled
- Forwards them to an MQTT broker (local or cloud)
- Receives messages from the MQTT broker and injects them into the LoRa network (on channels with downlink enabled)
- Bridges your mesh to the global Meshtastic network (if using the public MQTT server)

Note: uplink and downlink are configured **per channel** and both default to **OFF**. MQTT only carries traffic for a channel once the MQTT module is enabled *and* that channel has uplink (and/or downlink) turned on.

## Requirements

- A WiFi-capable Meshtastic node (T-Beam, Heltec V3, T-Beam Supreme - all have WiFi)
- WiFi network with internet access at the gateway location
- An MQTT broker: either the public Meshtastic broker (mqtt.meshtastic.org) or a self-hosted one

## Configuration Steps

### Option A: Using the Meshtastic Public MQTT Server

The public broker `mqtt.meshtastic.org` is **not anonymous** - it requires username `meshdev` / password `large4cats` (these are also the firmware's built-in defaults).

```
# Configure via CLI:
meshtastic --set mqtt.enabled true
meshtastic --set mqtt.address mqtt.meshtastic.org
meshtastic --set mqtt.username meshdev
meshtastic --set mqtt.password large4cats

# Configure which channels to bridge (uplink/downlink are per-channel and default OFF)
meshtastic --ch-index 0 --ch-set uplink_enabled true
meshtastic --ch-index 0 --ch-set downlink_enabled true

# Configure WiFi (if not already done)
meshtastic --set network.wifi_ssid "YourSSID"
meshtastic --set network.wifi_psk "YourPassword"
```

**Privacy note:** The public MQTT server relays messages globally. Only use it for the default (unencrypted) channel unless you want your encrypted channel traffic relayed globally.

### Option B: Self-Hosted Mosquitto MQTT Broker

```
# Install Mosquitto on Raspberry Pi or VPS:
sudo apt install mosquitto mosquitto-clients
sudo systemctl enable --now mosquitto

# Mosquitto 2.0+ defaults allow_anonymous to FALSE and binds localhost-only.
# A bare install rejects LAN clients until you define a listener. Create
# /etc/mosquitto/conf.d/local.conf with:
#   listener 1883 0.0.0.0
#   allow_anonymous true   # trusted LAN only; otherwise use a password_file
# For authentication instead of anonymous access:
#   mosquitto_passwd -c /etc/mosquitto/passwd youruser
#   password_file /etc/mosquitto/passwd
#   allow_anonymous false
sudo systemctl restart mosquitto

# Configure node to use your local broker:
meshtastic --set mqtt.address 192.168.1.100
meshtastic --set mqtt.enabled true
```

## Verifying the Gateway is Working

```
# Subscribe to all Meshtastic MQTT topics and watch for packets
# (the public broker requires credentials):
mosquitto_sub -h mqtt.meshtastic.org -u meshdev -P large4cats -t "msh/US/2/e/#" -v

# Or on your local broker:
mosquitto_sub -h 192.168.1.100 -t "msh/#" -v
```

You should see packets appearing as mesh traffic is received. On the `/e/` (encrypted) topic these are raw binary protobuf-encoded ServiceEnvelope messages, not base64 text.

## MQTT Packet Decoding

Meshtastic MQTT packets on the `/e/` topic are protobuf-encoded. The Python `meshtastic` package has **no turnkey MQTT class** - the documented approach is to subscribe with **paho-mqtt** and decode the payload with the bundled protobuf bindings (`meshtastic.protobuf`):

```
pip install meshtastic paho-mqtt

import paho.mqtt.client as mqtt
from meshtastic.protobuf import mqtt_pb2  # ServiceEnvelope lives in mqtt.proto

def on_message(client, userdata, msg):
    # The /e/ payload is RAW BINARY protobuf - feed it straight in, do not base64-decode it
    envelope = mqtt_pb2.ServiceEnvelope()
    envelope.ParseFromString(msg.payload)
    print(envelope)

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()
```

Note: `from meshtastic.mqtt import MQTT` does not exist - there is no built-in MQTT client class in the library.