Files

287 lines
8.6 KiB
Markdown

# meshtastic.el
Chat client for [Meshtastic](https://meshtastic.org/) LoRa mesh networks in Emacs, inspired by ERC and rcirc.
Requires Emacs 28.1 or later and the [meshtastic Python package](https://python.meshtastic.org/).
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
```
1. A **Meshtastic device** sends and receives messages over LoRa radio.
2. **meshtastic-bridge.py** connects to the device via serial, forwards received messages as JSON lines to stdout, and accepts JSON commands on stdin.
3. **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 `RET` in 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 `t` on 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:
```sh
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+)
```elisp
(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
```elisp
(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`:
```sh
git clone https://git.andros.dev/andros/meshtastic.el.git
```
Add to your init file:
```elisp
(add-to-list 'load-path "/path/to/meshtastic.el")
(require 'meshtastic)
(setq meshtastic-serial-port "/dev/ttyUSB0")
```
## Usage
1. Set `meshtastic-serial-port` to match your device (e.g. `/dev/ttyUSB0` on Linux, `/dev/cu.usbserial-*` on macOS).
2. Run `M-x meshtastic` to open the welcome screen. The bridge starts automatically.
3. Press `c` to browse channels or `n` to browse nodes.
4. Press `RET` on a channel or node to open a chat buffer.
5. Type your message and press `RET` to send.
6. Run `M-x meshtastic-disconnect` to 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:
```elisp
(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:
```elisp
(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](https://docs.astral.sh/uv/),
clone the repository (it includes a `pyproject.toml` with the `meshtastic`
dependency) and point the executable at `uv run python`:
```sh
git clone https://git.andros.dev/andros/meshtastic.el.git
cd meshtastic.el
uv sync # creates .venv and installs meshtastic
```
```elisp
(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`:
```elisp
(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 to `meshtastic-bridge.py`, auto-resolved from the package directory.
- `meshtastic-notify` (default `t`): enable desktop notifications (via D-Bus).
- `meshtastic-timestamp-format` (default `"%H:%M"`): format for message timestamps.
- `meshtastic-message-history` (default `200`): number of messages kept in memory per channel or DM.
## Development
### Running the tests
Install dev dependencies and run the test suite with [uv](https://docs.astral.sh/uv/):
```sh
uv sync --group dev
uv run pytest
```
Or with pip:
```sh
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/](https://www.gnu.org/licenses/).