SkillAgentSearch skills...

Meshcoretomqtt

A python based script to send meshore debug and packet capture data to MQTT for analysis Requires meshcore repeater to be connected to a raspberry pi, server or similar linux device able to run python.

Install / Use

/learn @Cisien/Meshcoretomqtt
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

meshcoretomqtt

A Python-based script to send MeshCore debug and packet capture data to MQTT for analysis. Requires a MeshCore repeater to be connected to a Raspberry Pi, server, or similar device running Python.

The goal is to have multiple repeaters logging data to the same MQTT server so you can easily troubleshoot packets through the mesh. You will need to build a custom image with packet logging and/or debug for your repeater to view the data.

One way of tracking a message through the mesh is filtering the MQTT data on the hash field as each message has a unique hash. You can see which repeaters the message hits!

Quick Install

One-Line Installation (Recommended)

curl -fsSL https://raw.githubusercontent.com/Cisien/meshcoretomqtt/main/install.sh | sudo bash

The installer will:

  • Create a dedicated mctomqtt system user
  • Install to /opt/mctomqtt/ with config at /etc/mctomqtt/
  • Guide you through interactive MQTT broker configuration
  • Set up Python virtual environment (requires Python 3.11+)
  • Configure a systemd service (Linux) or launchd daemon (macOS)
  • Auto-detect and migrate existing ~/.meshcoretomqtt installations

Custom Repository/Branch

Install from a fork or custom branch:

curl -fsSL https://raw.githubusercontent.com/yourusername/meshcoretomqtt/yourbranch/install.sh | \
  sudo bash -s -- --repo yourusername/meshcoretomqtt --branch yourbranch

Local Testing

git clone https://github.com/Cisien/meshcoretomqtt
cd meshcoretomqtt
sudo LOCAL_INSTALL=$(pwd) ./install.sh

NixOS

configuration.nix:

{ config, pkgs, meshcoretomqtt, ... }:

flake.nix

inputs = {
  meshcoretomqtt.url = "github:Cisien/meshcoretomqtt"
};

in your system config

imports = [inputs.meshcoretomqtt.nixosModules.default];
services.mctomqtt = {
    enable = true;
    iata = "FOO";
    serialPorts = ["/dev/ttyUSB0"];

    # Disable defaults if you like.
    # Defaults are used if nothing is specified
    defaults = {
        letsmesh-us.enable = true;
        letsmesh-eu.enable = true;
    };

    # Define custom brokers if you need them
    brokers = [
      {
        name = "my-broker";
        enabled = true;
        server = "mqtt.example.com";
        port = 1883;
        tls.enabled = true;
        auth = {
          method = "password";
          username = "my_username";
          password = "my_password";
        };
      }
    ];

    # Additional settings
    settings = {
      log-level = "DEBUG";
    };
  };

Prerequisites

Hardware Setup

  1. Setup a Raspberry Pi (Zero / 2 / 3 or 4 recommended) or similar Linux/macOS device

  2. Build/flash a MeshCore repeater with appropriate build flags:

    Recommended minimum:

    -D MESH_PACKET_LOGGING=1
    

    Optional debug data:

    -D MESH_DEBUG=1
    
  3. Plug the repeater into the device via USB (RAK or Heltec tested)

  4. Configure the repeater with a unique name as per MeshCore guides

Software Requirements

  • Python 3.11 or higher (required for tomllib stdlib module)
  • For auth token support (optional): Node.js and @michaelhart/meshcore-decoder

The installer handles these dependencies automatically!

Directory Layout

/opt/mctomqtt/              # App home (owned by mctomqtt:mctomqtt)
  mctomqtt.py               # Entry point
  bridge/                   # Core bridge package
  auth_token.py
  config_loader.py
  .version_info
  venv/                     # Python venv (pyserial, paho-mqtt)
  .nvm/                     # NVM + Node LTS + meshcore-decoder

/etc/mctomqtt/              # Config (owned root:mctomqtt, 755)
  config.toml               # Defaults (644, OVERWRITTEN on updates)
  config.d/                 # Drop-in override directory
    00-user.toml               # User config (644, never overwritten)

Configuration

Configuration uses TOML files with a layered override system:

  • /etc/mctomqtt/config.toml — Default values (overwritten on updates, do not edit)
  • /etc/mctomqtt/config.d/00-user.toml — Your custom configuration (never overwritten)

Files in config.d/ are loaded alphabetically and deep-merged over the defaults.

To bypass the default config loading entirely, use --config:

mctomqtt.py --config /path/to/config.toml
mctomqtt.py --config /path/to/base.toml --config /path/to/overrides.toml

When --config is used, /etc/mctomqtt/ is not read. Multiple --config flags are supported; files are loaded in order with later files overlaying earlier ones.

Editing Configuration

sudo nano /etc/mctomqtt/config.d/00-user.toml

Basic Example (00-user.toml)

[general]
iata = "SEA"

[serial]
ports = ["/dev/ttyACM0"]

[[broker]]
name = "my-mqtt"
enabled = true
server = "mqtt.example.com"
port = 1883

[broker.auth]
method = "password"
username = "my_username"
password = "my_password"

Advanced Example with Multiple Brokers

[general]
iata = "SEA"

[serial]
ports = ["/dev/ttyACM0"]

# Local MQTT with Username/Password
[[broker]]
name = "local-mqtt"
enabled = true
server = "mqtt.local"
port = 1883

[broker.auth]
method = "password"
username = "localuser"
password = "localpass"

# LetsMesh.net Packet Analyzer (US)
[[broker]]
name = "letsmesh-us"
enabled = true
server = "mqtt-us-v1.letsmesh.net"
port = 443
transport = "websockets"

[broker.tls]
enabled = true

[broker.auth]
method = "token"
audience = "mqtt-us-v1.letsmesh.net"

Topic Templates

Topics support template variables:

  • {IATA} — Your 3-letter location code
  • {PUBLIC_KEY} — Device public key (auto-detected)

Global topics (in config.toml defaults):

[topics]
status = "meshcore/{IATA}/{PUBLIC_KEY}/status"
packets = "meshcore/{IATA}/{PUBLIC_KEY}/packets"
debug = "meshcore/{IATA}/{PUBLIC_KEY}/debug"

Per-broker topic overrides (optional):

[[broker]]
name = "custom-broker"
enabled = true
server = "mqtt.example.com"

[broker.topics]
status = "custom/{IATA}/{PUBLIC_KEY}/status"
iata = "LAX"

Authentication Methods

1. Username/Password

[[broker]]
name = "my-broker"
enabled = true
server = "mqtt.example.com"

[broker.auth]
method = "password"
username = "your_username"
password = "your_password"

2. Auth Token (Public Key Based)

Requires @michaelhart/meshcore-decoder and firmware supporting get prv.key command.

[[broker]]
name = "letsmesh-us"
enabled = true
server = "mqtt-us-v1.letsmesh.net"
port = 443
transport = "websockets"

[broker.tls]
enabled = true

[broker.auth]
method = "token"
audience = "mqtt-us-v1.letsmesh.net"

The script will:

  • Read the private key from the connected MeshCore device via serial
  • Generate JWT auth tokens using the device's private key
  • Authenticate using the v1_{PUBLIC_KEY} username format

Note: The private key is read directly from the device and used for signing only. It's never transmitted or saved to disk.

To install meshcore-decoder:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
# Restart shell or: source ~/.bashrc
nvm install --lts
npm install -g @michaelhart/meshcore-decoder

Additional Settings

[general]
# Logging level: DEBUG, INFO, WARNING, ERROR, CRITICAL
log_level = "INFO"

# Wait for system clock sync before setting repeater time (default: true)
# Set to false on systems without timedatectl or NTP
sync_time = true

Remote Serial (LetsMesh.net Experimental)

Remote serial allows you to execute serial commands on your node remotely via the LetsMesh.net MeshCore Packet Analyzer web interface. Commands are cryptographically signed by an authorized companion device connected via Bluetooth.

Security Model:

  • Commands must be signed with an Ed25519 private key
  • Only companions in the allowlist can send commands
  • Each command JWT has a 30-second expiry (checked against system clock)
  • Nonces prevent replay attacks
  • Responses are signed by the node's private key for end-to-end verification

Configuration:

[remote_serial]
enabled = true
allowed_companions = [
    "03CEBEA3DA9C279CF8EB9449F0CC5BA3690621EE66A3B91067CDBA881EC883A5"
]
nonce_ttl = 120
command_timeout = 10

How it works:

  1. You connect your companion device via Bluetooth to the Packet Analyzer web interface
  2. The browser uses the companion's private key to sign command JWTs
  3. Commands are sent via MQTT to your node's serial/commands topic
  4. This script verifies the JWT signature against the allowlist
  5. Valid commands are executed on the serial port
  6. Responses are signed and published to the serial/responses topic

Note: Ensure your system clock is synchronized (NTP) for JWT expiry verification.

Running the Script

The installer offers three deployment options:

1. System Service (Recommended)

Automatically starts on boot and runs as a dedicated system user.

Linux (systemd):

sudo systemctl start mctomqtt      # Start service
sudo systemctl stop mctomqtt       # Stop service
sudo systemctl status mctomqtt     # Check status
sudo systemctl restart mctomqtt    # Restart service
sudo journalctl -u mctomqtt -f     # View logs

macOS (launchd):

sudo launchctl load /Library/LaunchDaemons/com.meshcore.mctomqtt.plist
sudo launchctl unload /Library/LaunchDaemons/com.meshcore.mctomqtt.plist
sudo launchctl list | grep mctomqtt
tail -f /var/log/mctomqtt.log

2. Docker Container

# Build the image
docker build -t mctomqtt:latest /path/to/meshcoretomqtt

# Run the container
docker run -d \
  --name mctomqtt \
  --restart unless-stopped \
  -v /path/to/config.toml:/etc/mctomqtt/config.toml \
  --device=/dev/ttyACM0 \
  mctomqtt:latest

3. Manual Execution

cd /opt/mctomqtt
sudo -u mctomqtt ./venv/bin/python3 mctomqtt.py --config /etc/mctomqtt/config.toml

With debug output:

sudo -u mctomqtt ./venv/bin/python3 mctomqtt.py --config /etc/m
View on GitHub
GitHub Stars67
CategoryDevelopment
Updated21h ago
Forks23

Languages

Python

Security Score

95/100

Audited on Apr 3, 2026

No findings