Source Code
Meshtastic Skill
Control a Meshtastic node via USB for off-grid LoRa mesh communication.
Prerequisites
- Meshtastic-compatible hardware (RAK4631, T-Beam, Heltec, LilyGo, etc.)
- USB connection to host machine
- Python 3.9+ with
meshtasticandpaho-mqttpackages - See
references/SETUP.mdfor full installation guide
Configuration
Edit CONFIG.md with your node details, MQTT settings, and alert destinations.
Architecture
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ MQTT Bridge โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ RECEIVE: mqtt.meshtastic.org (global JSON traffic) โ
โ PUBLISH: optional map broker (protobuf) โ
โ SOCKET: localhost:7331 (commands: send, status, toggle) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Files: โ
โ โข /tmp/mesh_messages.txt - received messages log โ
โ โข /tmp/mesh_nodes.json - cached node positions โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโ USB โโโโโโโโโโโโโโโ
โ LoRa Node โโโโโโโโโโโโโโโบโ Bridge.py โ
โ (Radio) โ โ - Serial โ
โโโโโโโโโโโโโโโ โ - Socket โ
โ - MQTT โ
โโโโโโโโฌโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ โ
โผ โผ โผ
localhost:7331 /tmp/mesh_* MQTT Broker
(send commands) (message logs) (mesh traffic)
Quick Reference
Send Messages
# Via socket (preferred - works while bridge running)
echo '{"cmd":"send","text":"Hello mesh!"}' | nc -w 2 127.0.0.1 7331
# Direct message to specific node
echo '{"cmd":"send","text":"Hey!","to":"!abcd1234"}' | nc -w 2 127.0.0.1 7331
# Check status
echo '{"cmd":"status"}' | nc -w 2 127.0.0.1 7331
# List RF nodes (seen via radio)
echo '{"cmd":"nodes"}' | nc -w 2 127.0.0.1 7331
Map Visibility (if configured)
# Toggle map publishing on/off
echo '{"cmd":"map"}' | nc -w 2 127.0.0.1 7331
# Explicitly enable/disable
echo '{"cmd":"map","enable":true}' | nc -w 2 127.0.0.1 7331
echo '{"cmd":"map","enable":false}' | nc -w 2 127.0.0.1 7331
# Force immediate position report
echo '{"cmd":"map_now"}' | nc -w 2 127.0.0.1 7331
Read Messages
# Recent messages (last 20)
tail -20 /tmp/mesh_messages.txt
# Filter common noise
tail -50 /tmp/mesh_messages.txt | grep -v -E "(Hello!|hey|mqtt-test)"
Message Log Format
TIMESTAMP|CHANNEL|SENDER|DISTANCE|TEXT
2026-02-02T12:43:59|LongFast|!433bf114|1572km|Moin moin!
Bridge Service
# Status
sudo systemctl status meshtastic-bridge
# Restart
sudo systemctl restart meshtastic-bridge
# View logs
sudo journalctl -u meshtastic-bridge -f
# Stop (needed for direct CLI access)
sudo systemctl stop meshtastic-bridge
Monitoring & Alerts
Option 1: Cron Job (Recommended)
cron.add({
name: "mesh-monitor",
schedule: { kind: "every", everyMs: 300000 }, // 5 min
sessionTarget: "isolated",
payload: {
kind: "agentTurn",
message: "Check /tmp/mesh_messages.txt for new messages. Filter out noise (test messages, 'Hello!', 'hey'). Alert me of interesting ones with translations if non-English.",
timeoutSeconds: 60,
deliver: true,
channel: "telegram" // or your channel
}
})
Option 2: Digest Summary
cron.add({
name: "mesh-digest",
schedule: { kind: "cron", expr: "0 8,14,20 * * *", tz: "Europe/Madrid" },
sessionTarget: "isolated",
payload: {
kind: "agentTurn",
message: "Read /tmp/mesh_messages.txt. Create a digest of interesting messages from the last 6 hours. Translate non-English, guess country from distance. Post summary.",
timeoutSeconds: 120,
deliver: true,
channel: "telegram"
}
})
Option 3: Spawned Monitor Agent
sessions_spawn({
task: "Monitor /tmp/mesh_messages.txt every 30 seconds. Alert me for interesting messages (not noise). Run for 1 hour.",
label: "mesh-monitor",
runTimeoutSeconds: 3600
})
Distance Reference
Approximate distances for country guessing (adjust for your location):
| Distance | Typical Regions |
|---|---|
| <500km | Neighboring countries/regions |
| 500-1000km | Medium range |
| 1000-1500km | Long range |
| 1500-2000km | Very long range (likely MQTT relay) |
| >2000km | MQTT-bridged traffic |
Privacy Notes
- Map reports can use fuzzy positioning (~2km precision)
- Position publishing can be toggled off entirely
- Local RF messages are logged but not shared externally by default
- Never broadcast precise location in messages
Supported Hardware
| Device | Notes |
|---|---|
| RAK4631 | Recommended, reliable USB |
| T-Beam | Popular, has GPS |
| Heltec V3 | Budget option |
| LilyGo T-Echo | E-paper display |
See references/SETUP.md for hardware-specific setup.
Regional Frequencies
| Region | Frequency | Topic Root |
|---|---|---|
| Europe | 868 MHz | msh/EU_868/2/json |
| Americas | 915 MHz | msh/US/2/json |
| Australia/NZ | 915 MHz | msh/ANZ/2/json |
Files
~/.openclaw/skills/meshtastic/
โโโ SKILL.md # This file
โโโ CONFIG.md # Your configuration
โโโ scripts/
โ โโโ mesh.py # CLI wrapper
โโโ references/
โโโ SETUP.md # Installation guide
Troubleshooting
"Resource temporarily unavailable"
- Only one process can use serial port at a time
- Stop bridge before direct CLI:
sudo systemctl stop meshtastic-bridge
No messages appearing
- Check MQTT subscription topic matches your region
- Verify firewall allows outbound port 1883
- Check
journalctl -u meshtastic-bridgefor errors
Can't send messages
- Ensure bridge is running (socket server)
- Check serial port path in config
- Try:
echo '{"cmd":"status"}' | nc -w 2 127.0.0.1 7331
Considering BLE instead of USB?
- Don't. USB is far more reliable on Linux.
- BLE on Linux (BlueZ/bleak) has notification bugs, pairing inconsistencies, and random disconnects.
- See
references/SETUP.mdfor detailed findings.