Skip to main content

Meshtastic Python Scripting

The meshtastic Python Library

The official meshtastic Python library provides programmatic access to any Meshtastic device. It exposes the full device API: sending and receiving messages, reading/writing configuration, querying node lists, requesting telemetry, and subscribing to real-time events.

Install:

pip install meshtastic

Official documentation: python.meshtastic.org
Source and examples: github.com/meshtastic/python

Connection Types

Interface classTransportTypical use
meshtastic.SerialInterface USB serial (CDC ACM) Direct connection, most reliable; default port auto-detected or specify /dev/ttyUSB0
meshtastic.TCPInterface TCP/IP over WiFi Wireless connection to nodes with WiFi enabled (ESP32 devices); specify host IP
meshtastic.BLEInterface Bluetooth Low Energy Short-range wireless; requires BlueZ on Linux or appropriate BLE stack

Basic Usage Examples

Connect and print node info

import meshtastic
import meshtastic.serial_interface

iface = meshtastic.serial_interface.SerialInterface()
print(iface.nodes)          # dict of all known nodes
iface.close()

Send a message to a channel

iface.sendText("Hello mesh", channelIndex=0)
# channelIndex=0 is the primary channel; 1–7 are secondary channels

Subscribe to received messages

from pubsub import pub

def on_receive(packet, interface):
    print(f"Received: {packet}")

pub.subscribe(on_receive, "meshtastic.receive")

# Keep the script alive while the interface thread processes events
import time
try:
    while True:
        time.sleep(1)
except KeyboardInterrupt:
    iface.close()

Get node list with last-heard timestamps

for node_id, node in iface.nodes.items():
    last_heard = node.get("lastHeard", "unknown")
    name = node.get("user", {}).get("longName", node_id)
    print(f"{name}: last heard {last_heard}")

Read and write device config

local = iface.getNode('^local')
# Read
print(local.localConfig.lora.hop_limit)
# Write
local.localConfig.lora.hop_limit = 5
local.writeConfig("lora")

Request position from local node

iface.getNode('^local').requestPosition()

Automation Use Cases

  • Automated mesh announcements — scheduled weather, news, or system status broadcasts on a channel.
  • Message bridges — relay messages between the mesh and Telegram, Discord, Matrix, or SMS (via Twilio). Bidirectional bridging is straightforward with the pub/sub event model.
  • Telemetry logging — write incoming telemetry packets to CSV, SQLite, or InfluxDB for Grafana dashboards.
  • Position mapping — forward GPS positions to a self-hosted map (e.g. Traccar, OwnTracks, or a custom Leaflet map).
  • Alert systems — trigger SMS or email alerts when specific nodes go offline (last-heard threshold exceeded).
  • Config management — script bulk configuration changes across many nodes connected via TCP.

Common Patterns

# Connect (serial, auto-detect port)
iface = meshtastic.serial_interface.SerialInterface()

# Connect (TCP)
iface = meshtastic.tcp_interface.TCPInterface("192.168.1.50")

# Send text
iface.sendText("Hello mesh", channelIndex=0)

# Send to specific node (DM)
iface.sendText("Private message", destinationId="!a1b2c3d4", channelIndex=0)

# Request position
iface.getNode('^local').requestPosition()

# Write channel config
ch = iface.getNode('^local').channels[0]
ch.settings.name = "MyNet"
iface.getNode('^local').writeChannel(0)

Event Loop and Threading

The library spawns a background thread that reads from the serial/TCP connection and dispatches events via the PyPubSub pub/sub system. Your on_receive callback is called in that background thread — use thread-safe data structures (queues, locks) if you share state with your main thread.

Available topics:

  • meshtastic.receive — any incoming packet
  • meshtastic.receive.text — text messages only
  • meshtastic.receive.position — position updates
  • meshtastic.receive.telemetry — telemetry packets
  • meshtastic.connection.established — fired after successful connect
  • meshtastic.connection.lost — fired on disconnect

Error Handling

  • Serial port busy: only one process can hold the serial port. Close the Meshtastic app or CLI before running your script. Use try/finally: iface.close() to release cleanly.
  • Reconnection on disconnect: subscribe to meshtastic.connection.lost and re-instantiate the interface with exponential backoff.
  • Timeout handling: SerialInterface has a noProto parameter — set it to True for raw serial access without waiting for a Meshtastic handshake, useful for debugging hardware.
  • Packet validation: always guard against missing keys in the packet dict; not all fields are present in every packet type.