Meshtastic
Bridge Meshtastic to Home Assistant with MQTT
Connect an ESP32 gateway to a local Mosquitto broker for battery graphs and telemetry. Set privacy-safe uplinks and add selected-message alerts.

OUR PICKS / YOUR SETUP
Find your fit.
Use a Heltec V3 Wi-Fi gateway and your own Mosquitto broker, with only the intended channel uplinked and every downlink off. JSON is plaintext; keep it local. Use the custom HACS integration for direct-message triggers at the recipient gateway.
- Gateway for JSONESP32 / Wi-Fi
- Start directionUplink only
- BrokerLocal Mosquitto
A radio message doesn’t need to stop at your phone. Give one Meshtastic node a path to a local MQTT broker and you can graph battery readings, watch telemetry and get the useful messages in Home Assistant—without building a public radio-to-internet bridge.
This research guide uses Meshtastic, Home Assistant and maker documentation, checked October 29, 2026.
Start with a Heltec V3 Wi-Fi gateway, your own Mosquitto broker and uplink only. Keep radio downlinks off while you build the dashboard. For direct-message alerts, the Meshtastic project’s custom HA integration offers a separate connection to the recipient node.
What the MQTT module actually does
Meshtastic’s MQTT module bridges radio packets and a broker. Its integration reference explains the two directions:
| Setting | What it changes | Starting choice |
|---|---|---|
| Channel uplink | Publishes that channel’s packets to MQTT | On only for the controlled channel you intend to bridge |
| Channel downlink | Accepts broker traffic for transmission onto LoRa | Off on every channel |
| Encryption Enabled | Keeps the protobuf packet payload encrypted for MQTT | On |
| JSON Enabled | Adds readable JSON for supported packet types | On only to your access-controlled local broker |
| TLS Enabled | Protects the broker connection in transit | Configure for your broker’s TLS setup |
| Map Reporting | Publishes a separate unencrypted map report | Off |
JSON packets are plaintext, even with Encryption Enabled on. That encryption switch protects the protobuf route, not the JSON copy. TLS protects transport; broker access and retained data still matter.
JSON generation isn’t supported on nRF52 devices such as the RAK4631. An ESP32 gateway can receive another node’s telemetry over LoRa and produce the JSON for HA. Giving an nRF52 board Ethernet or a phone proxy doesn’t add JSON generation to its firmware.
Keep the broker yours
Use the official Mosquitto Broker app, formerly called the add-on. Home Assistant’s MQTT documentation recommends a private broker and can configure Mosquitto alongside the MQTT integration.
A local broker gives you control of logins, topic access and storage. Never uplink a private channel to a public broker. Use a dedicated, controlled bench channel with a generated private key for the lesson below; don’t reuse the default public key on a private broker. Meshtastic warns that private brokers don’t enforce the public server’s traffic restrictions and can flood the radio mesh.
Leave downlinks off and skip automatic echo replies. A battery graph needs incoming readings, not an acknowledgement transmitted over LoRa for every database update. The channels and encryption guide covers the radio side of that choice.
Buy the gateway; reuse the HA host if you have one
The Heltec WiFi LoRa 32 V3 is the ESP32-based Wi-Fi board pick from our starter-node guide. It needs USB power, a matching antenna and a suitable case. Attach the antenna before powering the radio, and set the region your country permits; the linked board is the US 915 MHz version.
Already have Home Assistant, including HA Green? Keep it. If you need a host, use the complete Pi 5 NVMe stack below—not just a bare board.
We earn a commission if you buy through our links — it doesn’t change our picks. How we make money → As an Amazon Associate I earn from qualifying purchases.
| Job | Part | Fit |
|---|---|---|
| Wi-Fi gateway | Heltec WiFi LoRa 32 V3, 915 MHz | ESP32-S3 board for the JSON route |
| HA host board | Raspberry Pi 5 8GB | Board for the complete NVMe host |
| Power | Official 27W USB-C PSU | Official 5.1 V, 5 A supply |
| Cooling | Raspberry Pi Active Cooler | Fits beneath the standard HAT+ |
| Storage adapter | Raspberry Pi M.2 HAT+ | Standard version: 2230/2242 drives only |
| SSD | Kingston NV3 500GB 2230 | SNV3SM3/500G, the short drive |
| Open case base | Official Pi 5 case | Remove its lid and supplied fan for the standard HAT+ stack |
| Integrated case alternative | Argon ONE V3 M.2 | Replaces the HAT+, cooler and official case with its own adapter/cooling; still needs Pi, PSU and SSD |
Follow the complete Pi 5 NVMe build for installation and Argon’s HA OS fan/control trade-offs. The solar relay guide covers the remote node’s power system; the mains-powered gateway does the Wi-Fi work here.
Connect Mosquitto and the radio
Periodic battery and voltage telemetry travels on the source node’s primary channel. The gateway must share that channel’s name and key and uplink it; adding a secondary chat channel on the source won’t move its telemetry there. Renaming a primary channel changes its automatically selected frequency slot, so explicitly match the slot when the nodes have different primary names. The Meshtastic channel guide explains both settings.
- In HA, install and start Mosquitto under Settings → Apps → Install app. Review its logs, then configure the discovered MQTT integration under Settings → Devices & services.
- Give the gateway its own broker login. The Mosquitto app documentation covers additional logins and topic ACLs. Limit that login to the topics it needs; keep credentials and channel keys out of screenshots.
- Enable the node’s Wi-Fi connection and enter your local broker’s address, username and password in MQTT settings. Don’t use
localhoston the radio—that means the radio itself. Don’t leave the address blank: Meshtastic then uses its default public server. - Enable the MQTT module, JSON and protobuf encryption. Set a distinct root topic; the fictional example below uses
labdemo. Leave Map Reporting off. - For the example below, make BenchChat the primary channel on both SolarDemo and the gateway, with the same generated private key and modem preset. Enable uplink on the gateway’s BenchChat channel and leave all downlinks off. Save, then use MQTT’s Listen to a topic tool in HA to inspect its JSON.
A supported Ethernet gateway is another network path, with its own hardware requirements. MQTT Client Proxy instead uses the connected client’s network connection, such as your phone’s. It depends on that client remaining connected, so use the independent Wi-Fi gateway for a dashboard you want available without your phone.
Turn one JSON reading into a battery sensor
Everything in this example—names, IDs, topics and values—is invented. SolarDemo has ID !12345678, or decimal 305419896. Both nodes use the BenchChat primary channel configured above. The publishing gateway is !87654321:
labdemo/2/json/BenchChat/!87654321
The final topic segment identifies the gateway. The JSON from field identifies the originating node; don’t mistake one for the other.
{"type":"telemetry","from":305419896,"sender":"!87654321","payload":{"battery_level":72,"voltage":4.01}}
The raw topic mixes message types. Filter real battery samples into a separate local topic so a text packet doesn’t refresh the battery sensor’s expiry. Paste this into an automation’s YAML editor, then substitute your own root, channel and originating ID:
alias: Mesh demo battery to sensor
triggers:
- trigger: mqtt
topic: "labdemo/2/json/BenchChat/+"
conditions:
- condition: template
value_template: >-
{% set p = trigger.payload_json | default(none) %}
{{ p is mapping and p.get('type') == 'telemetry'
and p.get('from') == 305419896
and p.get('payload') is mapping
and is_number(p.payload.get('battery_level'))
and p.payload.battery_level is not boolean
and 0 <= (p.payload.battery_level | float) <= 100 }}
actions:
- action: mqtt.publish
data:
topic: "labdemo/ha/solar_demo/battery"
payload: "{{ trigger.payload_json.payload.battery_level }}"
retain: false
mode: queued
Then merge this into your existing mqtt: configuration in configuration.yaml—don’t create a second top-level mqtt: key:
mqtt:
sensor:
- name: "Solar Demo Battery"
unique_id: meshtastic_demo_battery
state_topic: "labdemo/ha/solar_demo/battery"
device_class: battery
state_class: measurement
unit_of_measurement: "%"
expire_after: 10800
Save, check your HA configuration, then run mqtt.reload from Developer tools → Actions, or restart Home Assistant.
Example assumptions: hourly battery reports, expiry after three missed reports, an alert below 20% and rearm above 25%. Three hours is 10,800 seconds; choose expiry for your actual reporting schedule rather than increasing radio traffic to feed a graph. Use non-retained measurement messages and clear retained test samples before testing expiry.
Home Assistant documents MQTT triggers, templates and MQTT sensor expiry. Keep out-of-range or missing battery values out of this percentage sensor, rather than converting them to zero.
For channel text, duplicate the normalizer with type: text, your allowed from ID and a string-valued payload.text; publish that text to a separate local topic. Add another MQTT sensor for it without a measurement class or percent unit. Meshtastic’s HA examples show the text and telemetry fields. Environmental temperature/humidity needs the corresponding sensor hardware and payload; it isn’t supplied by every node.
The custom integration: useful for direct messages
The Meshtastic project hosts meshtastic/home-assistant, a custom community integration installed through HACS, not a Home Assistant core integration. It connects directly to a radio over TCP, serial or Bluetooth, imports selected nodes and exposes metrics, message triggers and actions. It can also act as an MQTT client proxy; it isn’t simply a replacement JSON sensor parser.
For private-message alerts, connect it to the recipient gateway and select the gateway’s Receives Message on [DM entity] trigger (direct_message.received, filtered by entity_id). Remote nodes expose sent-message triggers. For a sender-specific alert, use the text-message event in the recipe below; the gateway device trigger has no sender field. Meshtastic’s encryption documentation explains that a node decrypts a message for its connected client, while current PKI DMs belong to their endpoints. Don’t expect MQTT JSON to reveal every DM overheard by the gateway.
The integration documents a single-client connection limit. Choose HA or the phone as the owner of that client connection; don’t make both compete for it.
Three useful automations
Low battery on a solar node. Trigger on each new message on the normalized battery topic. If the fresh value is below the example alert threshold and a “Battery alert sent” Toggle helper (created off) is off, notify your phone and turn the helper on. Reset it after a fresh reading above the rearm threshold. This catches a node that is already low on its next report, without depending on a threshold crossing or repeatedly buzzing for every sample.
A DM from one node. In Developer tools → Events, listen for meshtastic_api_text_message while the fictional FieldDemo sends a DM to the gateway. Copy the numeric data.from, data.to.node and data.gateway values into an Event trigger’s event-data filter; these select FieldDemo as sender and your gateway as both receiver and connected radio. Then send a Companion phone notification. Test with a FieldDemo DM, a different sender and a channel message: only the first should notify. Keep replies manual. The integration’s device triggers and text-message event implementation define these fields.
Log telemetry, not everything. Graph the battery sensor in History; apply the same filtered-topic pattern to voltage or a real environmental probe. HA’s Recorder stores history and supports exclusions. Exclude the last-text sensor if you don’t want message content in that archive, and review the custom integration’s separate message-log and position-tracker features.
Leave location out of the bridge
Map Reporting off stops the special map report, not ordinary position packets that might travel on an uplinked channel. Review position broadcasts and channel precision too. Bridge only nodes and data you own or have permission to publish; keep the bench example free of position data. Meshtastic also advises keeping private channel keys off unattended relay nodes—they can relay traffic without decrypting it.
Finish with a local test: publish the synthetic JSON to the fictional ingress topic, confirm the battery sensor updates, then try a text packet and confirm it doesn’t update the battery path. Test notifications to your phone without transmitting test floods over LoRa. Keep downlinks disabled and verify that no public broker or map feed is part of the configuration.
Explore the Meshtastic hub for the radio projects and the Home Assistant hub for the dashboard side. The win is a quiet little bridge: radio readings arrive, graphs move, and only the messages you chose interrupt your day.
Sources & methodology
Where these facts come from.
- Meshtastic: MQTT module settings and plaintext JSON · checked October 29, 2026
- Meshtastic: broker topics, packet fields and downlink behavior · checked October 29, 2026
- Home Assistant: local Mosquitto broker and MQTT integration · checked October 29, 2026
- Heltec: ESP32-S3 Wi-Fi gateway board · checked October 29, 2026
- Home Assistant: Mosquitto app logins and topic ACLs · checked October 29, 2026
- MQTT message received - Home Assistant · checked October 29, 2026
- Templating - Home Assistant · checked October 29, 2026
- MQTT Sensor - Home Assistant · checked October 29, 2026
- Home Assistant | Meshtastic · checked October 29, 2026
- Meshtastic project: custom HACS integration and message triggers · checked October 29, 2026
- Meshtastic integration device triggers · checked October 30, 2026
- Meshtastic integration text-message event · checked October 30, 2026
- Meshtastic: endpoint encryption and private-key handling · checked October 29, 2026
- Introduction | Home Assistant Companion Docs · checked October 29, 2026
- Recorder - Home Assistant · checked October 29, 2026
- Meshtastic: primary-channel telemetry and position precision · checked October 29, 2026
- Raspberry Pi: standard HAT+, cooler and case fit · checked October 29, 2026
- Kingston: NV3 2230 specifications · checked October 29, 2026
- Argon: integrated NVMe adapter and cooling · checked October 29, 2026
- Raspberry Pi: official 27W supply · checked October 29, 2026
Source dates are listed above.
As an Amazon Associate I earn from qualifying purchases.
Parts from this article
The gear, on Amazon
The models recommended above, with the specifications that matter for your choice.
DIY starter board
Heltec WiFi LoRa 32 V3, 915 MHz
Build around an OLED-equipped ESP32 board with Wi-Fi and a USB-C setup connection.
The board
Raspberry Pi 5 8GB
Quad-core Pi 5 with Gigabit Ethernet, PCIe for NVMe and USB ports for connected hardware.
Power
Raspberry Pi 27W USB-C Power Supply
The official 5.1 V 5 A supply for a Pi 5 driving an SSD and USB devices.
Cooling
Raspberry Pi Active Cooler
Clip-on heatsink and temperature-controlled fan; the M.2 HAT+ stacks above it.
NVMe adapter
Raspberry Pi M.2 HAT+
Connects a 2230 or 2242 NVMe SSD to the Pi 5 PCIe connector.
The SSD
Kingston NV3 500GB M.2 2230 NVMe SSD (SNV3SM3/500G)
A 2230 NVMe drive that fits the M.2 HAT+ and the Argon case.
Official case base
Official Raspberry Pi 5 case
Use the base for a standard M.2 HAT+ stack with the lid and supplied case fan removed.
Closed-case option
Argon ONE V3 M.2 NVMe case for Raspberry Pi 5
Aluminium case with its own NVMe board, fan and power button, for drives up to 2280.
Prepared by Racks & Watts.