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!
curl -fsSL https://raw.githubusercontent.com/Cisien/meshcoretomqtt/main/install.sh | bashUse a hosted configuration file for quick setup:
# Example: Local broker + LetsMesh.net Packet Analyzer MQTT server
curl -fsSL https://raw.githubusercontent.com/Cisien/meshcoretomqtt/main/install.sh | \
bash -s -- --config https://gist.githubusercontent.com/username/abc123/raw/my-config.envThis is useful for deploying multiple nodes with the same configuration.
Use your own configuration (Gist, repo, etc.):
curl -fsSL https://raw.githubusercontent.com/Cisien/meshcoretomqtt/main/install.sh | \
bash -s -- --config https://gist.githubusercontent.com/username/abc123/raw/my-config.envInstall from a fork or custom branch:
curl -fsSL https://raw.githubusercontent.com/yourusername/meshcoretomqtt/yourbranch/install.sh | \
bash -s -- --repo yourusername/meshcoretomqtt --branch yourbranchThe installer will:
- Guide you through interactive configuration (or use provided config)
- Set up Python virtual environment
- Configure one or multiple MQTT brokers
- Choose installation method: system service, Docker container, or manual
- Handle both Linux (systemd) and macOS (launchd)
- Auto-detect and update existing installations
If you want to test the installer locally:
git clone https://github.com/Cisien/meshcoretomqtt
cd meshcoretomqtt
LOCAL_INSTALL=$(pwd) ./install.sh-
Setup a Raspberry Pi (Zero / 2 / 3 or 4 recommended) or similar Linux/macOS device
-
Build/flash a MeshCore repeater with appropriate build flags:
Recommended minimum:
-D MESH_PACKET_LOGGING=1Optional debug data:
-D MESH_DEBUG=1 -
Plug the repeater into the device via USB (RAK or Heltec tested)
-
Configure the repeater with a unique name as per MeshCore guides
- Python 3.7 or higher
- For auth token support (optional): Node.js and
@michaelhart/meshcore-decoder
The installer handles these dependencies automatically!
Configuration uses environment files (.env and .env.local):
.env- Contains default values (don't edit, will be overwritten on updates).env.local- Your custom configuration (gitignored, never overwritten)
If you need to manually edit configuration after installation:
# Edit your local configuration
nano ~/.meshcoretomqtt/.env.local# Serial Configuration
SERIAL_PORTS=/dev/ttyACM0
# Global IATA Code (3-letter airport code for your location)
IATA=SEA
# MQTT Broker 1 - Username/Password
MQTT1_ENABLED=true
MQTT1_SERVER=mqtt.example.com
MQTT1_PORT=1883
MQTT1_USERNAME=my_username
MQTT1_PASSWORD=my_password# Serial Configuration
SERIAL_PORTS=/dev/ttyACM0
IATA=SEA
# Broker 1 - Local MQTT with Username/Password
MQTT1_ENABLED=true
MQTT1_SERVER=mqtt.local
MQTT1_PORT=1883
MQTT1_USERNAME=localuser
MQTT1_PASSWORD=localpass
# Broker 2 - Public Observer Network with Auth Token
MQTT2_ENABLED=true
MQTT2_SERVER=mqtt-us-v1.letsmesh.net
MQTT2_PORT=443
MQTT2_TRANSPORT=websockets
MQTT2_USE_TLS=true
MQTT2_USE_AUTH_TOKEN=true
MQTT2_TOKEN_AUDIENCE=mqtt-us-v1.letsmesh.netTopics support template variables:
{IATA}- Your 3-letter location code{PUBLIC_KEY}- Device public key (auto-detected)
Global topics (apply to all brokers by default):
TOPIC_STATUS=meshcore/{IATA}/{PUBLIC_KEY}/status
TOPIC_PACKETS=meshcore/{IATA}/{PUBLIC_KEY}/packets
TOPIC_DEBUG=meshcore/{IATA}/{PUBLIC_KEY}/debug
TOPIC_RAW=meshcore/{IATA}/{PUBLIC_KEY}/rawPer-broker topic overrides (optional):
# Broker 2 uses different topic structure
MQTT2_TOPIC_STATUS=custom/{IATA}/{PUBLIC_KEY}/status
MQTT2_TOPIC_PACKETS=custom/{IATA}/{PUBLIC_KEY}/data
MQTT2_IATA=LAX # Different IATA code for this brokerThis allows sending the same data to multiple brokers with different topic structures.
MQTT1_ENABLED=true
MQTT1_SERVER=mqtt.example.com
MQTT1_USERNAME=your_username
MQTT1_PASSWORD=your_passwordRequires @michaelhart/meshcore-decoder and firmware supporting get prv.key command.
MQTT1_ENABLED=true
MQTT1_SERVER=mqtt-us-v1.letsmesh.net
MQTT1_USE_AUTH_TOKEN=true
MQTT1_TOKEN_AUDIENCE=mqtt-us-v1.letsmesh.netThe 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-decoderThe installer offers three deployment options:
Automatically starts on boot and runs in the background.
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 logsmacOS (launchd):
launchctl start com.meshcore.mctomqtt # Start service
launchctl stop com.meshcore.mctomqtt # Stop service
launchctl list | grep mctomqtt # Check status
tail -f ~/Library/Logs/mctomqtt.log # View logsIsolated containerized deployment with automatic restarts.
If you prefer to run Docker manually without the installer:
# Build the image
docker build -t mctomqtt:latest /path/to/meshcoretomqtt
# Run the container
docker run -d \
--name mctomqtt \
--restart unless-stopped \
-v ~/.meshcoretomqtt/.env.local:/opt/.env.local \
--device=/dev/ttyACM0 \
mctomqtt:latestRun directly without service management.
cd ~/.meshcoretomqtt
./venv/bin/python3 mctomqtt.pyWith debug output:
./venv/bin/python3 mctomqtt.py -debugSimply re-run the installer - it will detect your existing installation and offer to update:
curl -fsSL https://raw.githubusercontent.com/Cisien/meshcoretomqtt/main/install.sh | bashOr for non-interactive updates (useful for scripts/automation):
curl -fsSL https://raw.githubusercontent.com/Cisien/meshcoretomqtt/main/install.sh | bash -s -- --updateThe updater will:
- Detect your existing service type (systemd/launchd/Docker)
- Stop the service/container
- Download and verify updated files
- Preserve your
.env.localconfiguration - Restart the service/container automatically
Install from a specific repo/branch:
# Using environment variables
INSTALL_REPO=yourusername/meshcoretomqtt INSTALL_BRANCH=yourbranch bash <(curl -fsSL https://raw.githubusercontent.com/yourusername/meshcoretomqtt/yourbranch/install.sh)
# Or using flags
curl -fsSL https://raw.githubusercontent.com/yourusername/meshcoretomqtt/yourbranch/install.sh | \
bash -s -- --repo yourusername/meshcoretomqtt --branch yourbranchIf you've cloned the repository:
cd meshcoretomqtt
LOCAL_INSTALL=$(pwd) ./install.shTo reconfigure without updating, either:
- Interactive: Re-run the installer and select "Reconfigure" when prompted
- Manual: Edit
.env.localdirectly and restart the service:nano ~/.meshcoretomqtt/.env.local # Then restart: sudo systemctl restart mctomqtt (or docker restart mctomqtt)
curl -fsSL https://raw.githubusercontent.com/Cisien/meshcoretomqtt/main/uninstall.sh | bashOr locally:
cd ~/.meshcoretomqtt
./uninstall.shThe uninstaller will:
- Stop and remove the service
- Offer to backup your
.env.local - Prompt before removing configuration
- Clean up all installed files
The installer can set up Docker automatically (option 2 during installation). For manual Docker setup:
- Create a configuration directory:
mkdir -p ~/mctomqtt-config- Create your
.env.localin the config directory:
cat > ~/mctomqtt-config/.env.local << 'EOF'
MCTOMQTT_SERIAL_PORTS=/dev/ttyACM0
MCTOMQTT_IATA=SEA
MCTOMQTT_MQTT1_ENABLED=true
MCTOMQTT_MQTT1_SERVER=mqtt.example.com
MCTOMQTT_MQTT1_USERNAME=user
MCTOMQTT_MQTT1_PASSWORD=pass
EOF- Build and run:
docker build -t mctomqtt:latest .
docker run -d --name mctomqtt \
-v ~/mctomqtt-config/.env.local:/opt/.env.local \
--device=/dev/ttyACM0 \
--restart unless-stopped \
mctomqtt:latestNote: Instead of /dev/ttyACM0 or similar, you can run ls /dev/serial/by-id/ -al to find the correct device and a more consistent device reference.
- View logs:
docker logs -f mctomqttNote: The installer handles all of this automatically, including interactive configuration!
This tool collects and forwards all packets transmitted over the MeshCore network. MeshCore does not currently have any privacy controls baked into the protocol to designate whether a user would like their data logged (#435). Privacy on MeshCore is provided by protecting secret channel keys - only packets encrypted with known channel keys can be decrypted and read. Packets on channels where you don't have the key will be forwarded as encrypted data.
-
Use a MQTT tool to view the packet data.
I recommend MQTTX
-
Data will appear in the following topics...
TOPIC_STATUS = "meshcore/status" TOPIC_RAW = "meshcore/raw" TOPIC_DEBUG = "meshcore/debug" TOPIC_PACKETS = "meshcore/packets" TOPIC_DECODED = "meshcore/decoded"Status: The last will and testement (LWT) of each node connected. Here you can see online / offline status of a node on the MQTT server.
RAW: The raw packet data going through the repeater.
DEBUG: The debug info (if enabled on the repeater build)
PACKETS: The flood or direct packets going through the repeater.
DECODED: Packets are decoded (and decrypted when the key is known) and pubished to this topic as unencrypted JSON
Note: origin is the repeater node reporting the data to mqtt. Not the origin of the LoRa packet.
Flood packet...
Topic: meshcore/packets QoS: 0
{"origin": "ag loft rpt", "timestamp": "2025-03-16T00:07:11.191561", "type": "PACKET", "direction": "rx", "time": "00:07:09", "date": "16/3/2025", "len": "87", "packet_type": "5", "route": "F", "payload_len": "83", "SNR": "4", "RSSI": "-93", "score": "1000", "hash": "AC9D2DDDD8395712"}
Direct packet...
Topic: meshcore/packets QoS: 0
{"origin": "ag loft rpt", "timestamp": "2025-03-15T23:09:00.710459", "type": "PACKET", "direction": "rx", "time": "23:08:59", "date": "15/3/2025", "len": "22", "packet_type": "2", "route": "D", "payload_len": "20", "SNR": "5", "RSSI": "-93", "score": "1000", "hash": "890BFA3069FD1250", "path": "C2 -> E2"}
- Complete more thorough testing
- Fix bugs with keepalive status topic