8.6 KiB
meshtastic.el
Chat client for Meshtastic LoRa mesh networks in Emacs, inspired by ERC and rcirc.
Requires Emacs 28.1 or later and the meshtastic Python package.
Connects directly to a Meshtastic device over USB serial. No server or intermediate proxy required.
How it works
LoRa Radio Python bridge Emacs
+-----------+ +-------------------+ +---------------+
| Meshtastic|<---->| meshtastic-bridge |<---->| meshtastic.el |
| Device | USB | .py (subprocess) | JSON | (this package)|
+-----------+ +-------------------+ +---------------+
Physical Reads/writes serial Chat buffers
device Emits JSON events Send / receive
- A Meshtastic device sends and receives messages over LoRa radio.
- meshtastic-bridge.py connects to the device via serial, forwards received messages as JSON lines to stdout, and accepts JSON commands on stdin.
- meshtastic.el spawns the bridge as a subprocess, parses its output, and provides chat buffers.
Buffers
Welcome screen (M-x meshtastic)
Meshtastic
======================================
Connection
Port: /dev/ttyUSB0
Status: Connected
Node: Hilltop Relay (!a1b2c3d4)
Statistics
Nodes: 12
Channels: 2
--------------------------------------
[c] Channels [n] Nodes
[g] Refresh [q] Quit
--------------------------------------
Channel list (M-x meshtastic-channels)
ID Name Role
0 LongFast Primary
1 HikingGroup Secondary
Node list (M-x meshtastic-nodes)
Hops Name Node ID Last heard
0 🟢 Hilltop Relay !a1b2c3d4 now
1 🟢 Solar Node 7 !d4e5f6a7 12m
2 🟢 BaseStation K9 !b8c9d0e1 5m
3 ⚫ Mountain Peak !f2a3b4c5 1h
Chat buffer (channel or DM)
[08:15] <Hilltop Relay> Good morning mesh!
[08:20] <BaseStation K9> Morning! Signal is great today
[08:21] <Solar Node 7> Copy that, 3 hops from here
[08:45] <Mountain Peak> Anyone near the trailhead?
[09:02] <Hilltop Relay> I can see 12 nodes from up here
[09:05] <BaseStation K9> Confirmed ✓
[09:30] <River Bridge> Just set up a new repeater
#LongFast> _
Features
- Channel list: browse available Meshtastic channels.
- Node list: browse all mesh nodes sorted by hop count, with online indicator and last-heard time.
- Chat buffers: read and send messages with an ERC-like prompt interface.
- Direct messages: open a DM chat with any node by pressing
RETin the node list. - Message buffer: messages received since the bridge started are kept in memory and shown when a chat buffer opens.
- Delivery indicator: sent messages show
·(sent to bridge),✓(bridge confirmed send) or✗(failed). - Desktop notifications: get notified when new messages arrive in background buffers.
- Input history: navigate previous inputs with
M-p/M-n. - Traceroute: press
ton a node to send a traceroute via the CLI.
Keymap
Channel and node list buffers
| Key | Description |
|---|---|
RET |
Open channel chat or DM with node |
0-7 |
Open channel by number (channels) |
t |
Send traceroute to node (nodes) |
g |
Refresh list from device |
q |
Quit buffer |
Chat buffers
| Key | Description |
|---|---|
RET |
Send message |
M-p |
Previous input from history |
M-n |
Next input from history |
C-c C-l |
Reload buffered messages |
Requirements
- Emacs 28.1+
- Python 3 with the meshtastic package:
pip install meshtastic
- A Meshtastic device connected via USB serial.
Installation
MELPA
M-x package-install RET meshtastic RET
use-package with :vc (Emacs 29+)
(use-package meshtastic
:vc (:url "https://git.andros.dev/andros/meshtastic.el"
:rev :newest)
:config
(setq meshtastic-serial-port "/dev/ttyUSB0"))
use-package with :load-path
(use-package meshtastic
:load-path "/path/to/meshtastic.el"
:config
(setq meshtastic-serial-port "/dev/ttyUSB0"))
Manual
Clone the repository and place the files on your load-path:
git clone https://git.andros.dev/andros/meshtastic.el.git
Add to your init file:
(add-to-list 'load-path "/path/to/meshtastic.el")
(require 'meshtastic)
(setq meshtastic-serial-port "/dev/ttyUSB0")
Usage
- Set
meshtastic-serial-portto match your device (e.g./dev/ttyUSB0on Linux,/dev/cu.usbserial-*on macOS). - Run
M-x meshtasticto open the welcome screen. The bridge starts automatically. - Press
cto browse channels ornto browse nodes. - Press
RETon a channel or node to open a chat buffer. - Type your message and press
RETto send. - Run
M-x meshtastic-disconnectto stop the bridge.
Configuration examples
Linux, pip
The default configuration works out of the box after pip install meshtastic.
Adjust the serial port to match your device:
(use-package meshtastic
:ensure t
:config
(setq meshtastic-serial-port "/dev/ttyUSB0"))
Find your port with ls /dev/ttyUSB* /dev/ttyACM* after plugging in the device.
macOS, pip
macOS serial ports use a cu.usbserial-* naming scheme:
(use-package meshtastic
:ensure t
:config
(setq meshtastic-serial-port "/dev/cu.usbserial-0001"))
Find your port with ls /dev/cu.usbserial-* or ls /dev/cu.SLAB_USBtoUART*.
macOS, uv
If you manage the Python environment with uv,
clone the repository (it includes a pyproject.toml with the meshtastic
dependency) and point the executable at uv run python:
git clone https://git.andros.dev/andros/meshtastic.el.git
cd meshtastic.el
uv sync # creates .venv and installs meshtastic
(use-package meshtastic
:load-path "~/path/to/meshtastic.el"
:config
(setq meshtastic-serial-port "/dev/cu.usbserial-0001")
(setq meshtastic-python-executable "/opt/homebrew/bin/uv run python"))
uv run python resolves the .venv from the package directory automatically;
no manual activation is needed.
Windows
Serial ports on Windows are named COMn:
(use-package meshtastic
:ensure t
:config
(setq meshtastic-serial-port "COM3")
(setq meshtastic-python-executable "python"))
Check Device Manager under "Ports (COM & LPT)" for the correct number.
Customization
Run M-x customize-group RET meshtastic RET to list all options.
Key options:
meshtastic-serial-port(default"/dev/ttyUSB0"): path to the serial device.meshtastic-python-executable(default"python3"): Python binary used to launch the bridge. Supports multi-word values such as"uv run python".meshtastic-bridge-script: path tomeshtastic-bridge.py, auto-resolved from the package directory.meshtastic-notify(defaultt): enable desktop notifications (via D-Bus).meshtastic-timestamp-format(default"%H:%M"): format for message timestamps.meshtastic-message-history(default200): number of messages kept in memory per channel or DM.
Development
Running the tests
Install dev dependencies and run the test suite with uv:
uv sync --group dev
uv run pytest
Or with pip:
pip install pytest
pytest
Notes
- No message history on startup: unlike server-based clients, the bridge has no database. Only messages received since the bridge started are available.
- LoRa bandwidth: Meshtastic uses LoRa radio, which has very limited bandwidth. Keep messages short.
- One device: each running instance connects to exactly one serial device.
License
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/.