MeshCore GUI — Native USB & BLE
Cross-frequency bridge included — no MQTT, no broker, no cloud. Just LoRa ↔ LoRa.
A graphical user interface for MeshCore mesh network devices with native USB serial and Bluetooth Low Energy (BLE) support, for on your desktop or as a headless service on your local network.
Table of Contents
- 1. Why This Project Exists
- 2. Features
- 3. Screenshots
- 4. Requirements
- 5. Installation
- 6. Usage
- 7. Starting the Application
- 7.1. Command-Line Options
- 7.2. Method 1: Interactive (foreground)
- 7.3. Method 2: Background with Visible Output
- 7.4. Method 3: Background with Terminal Free
- 7.5. Method 4: systemd Service
- 7.6. Accessing the Interface
- 7.7. Running Multiple Instances
- 7.8. Migrating Existing Data
- 7.9. Raspberry Pi 5 Notes
- 8. Configuration
- 9. Functionality
- 10. Architecture
- 11. Cross-Frequency Bridge
- 12. Known Limitations
- 13. Troubleshooting
- 14. Development
- 15. Roadmap
- 16. Disclaimer
- 17. License
- 18. Author
- 19. Acknowledgments
1. Why This Project Exists
MeshCore devices like the SenseCAP T1000-E can be managed through two interfaces: USB serial and BLE (Bluetooth Low Energy). The official companion apps communicate with devices over BLE, but they are mobile-only. For desktop or headless operation, USB serial is the most reliable option and works on all platforms.
This project provides a native desktop GUI that connects to your MeshCore device over USB serial or Bluetooth LE:
- Dual transport — auto-detects the connection type from the device argument: serial port path → USB serial, MAC address → Bluetooth LE
- Serial mode — requires Serial Companion firmware on the device; works on all platforms
- BLE mode — connects wirelessly via Bluetooth Low Energy with automatic PIN pairing; requires Linux with BlueZ (D-Bus). Note: recent BlueZ versions (5.66+) may cause connection instability — see 5.1. System Dependencies for details
- Cross-platform — written in Python using cross-platform libraries, runs on Linux, macOS and Windows (serial mode); BLE mode is Linux-only
- Headless capable — since the interface is web-based (powered by NiceGUI), it also runs headless on devices like a Raspberry Pi, accessible from any browser on your local network
- Message archive — all messages are persisted to disk with configurable retention, so you maintain a searchable history of mesh traffic
- Bots and observation — run a keyword-triggered auto-reply bot or passively observe mesh traffic 24/7
- Room Server support — login to Room Servers directly from the GUI with dedicated message panels per room
- Cross-frequency bridge — connect two MeshCore devices on different frequencies with an independent bridge daemon that forwards channel messages bidirectionally, with zero changes to the main codebase
Note: This project is under active development. Not all features from the official MeshCore Companion apps have been implemented yet. Contributions and feedback are welcome.
Note: This application has been tested on Linux (Ubuntu 24.04) and Raspberry Pi 5 (Debian Bookworm, headless) with both serial and BLE transports. macOS and Windows should work for serial mode since all dependencies (
nicegui,meshcore) are cross-platform, but this has not been verified. BLE mode requires Linux with BlueZ. Feedback and contributions for other platforms are welcome.
Under the hood it uses meshcore as the protocol layer, meshcoredecoder for raw LoRa packet decryption and route extraction, and NiceGUI for the web-based interface.
2. Features
- Real-time Dashboard — Device info, contacts, messages and RX log
- Interactive Map — Leaflet map with markers for own position and contacts
- Channel Messages — Send and receive messages on channels
- Direct Messages — Click on a contact to send a DM
- Contact Maintenance — Pin/unpin contacts to protect them from deletion, bulk-delete unpinned contacts from the device, and toggle automatic contact addition from mesh adverts
- Message Filtering — Filter messages per channel via checkboxes
- Message Route Visualization — Click any message to open a detailed route page showing the path (hops) through the mesh network on an interactive map, with a hop summary, route table and reply panel
- Message Archive — All messages and RX log entries are persisted to disk with configurable retention. Browse archived messages via the archive viewer with filters (channel, time range, text search), pagination and inline route tables
- Room Server Support — Login to Room Servers directly from the GUI. Each Room Server gets a dedicated panel with message display, send functionality and login/logout controls. Passwords are stored securely outside the repository. Message author attribution correctly resolves the real sender from signed messages
- Dynamic Channel Discovery — Channels are automatically discovered from the device at startup via probing, eliminating the need to manually configure
CHANNELS_CONFIG
- Keyword Bot — Built-in auto-reply bot that responds to configurable keywords on selected channels, with cooldown and loop prevention
- Packet Decoding — Raw LoRa packets from RX log are decoded and decrypted using channel keys, providing message hashes, path hashes and hop data
- Message Deduplication — Dual-strategy dedup (hash-based and content-based) prevents duplicate messages from appearing
- Local Cache — Device info, contacts and channel keys are cached to disk (
~/.meshcore-gui/cache/) so the GUI is instantly populated on startup from the last known state, even before the serial link connects. Contacts from the device are merged with cached contacts so offline nodes are preserved. Channel keys that fail to load at startup are retried in the background every 30 seconds - Periodic Contact Refresh — Contacts are automatically refreshed from the device at a configurable interval (default: 5 minutes) and merged with the cache
- Threaded Architecture — Device communication in separate thread for stable UI
- Dual Transport — Auto-detects USB serial or Bluetooth LE from the device argument; BLE includes automatic PIN pairing and bond management
- Cross-Frequency Bridge — Standalone bridge daemon (
meshcore_bridge) connects two devices on different frequencies by forwarding messages on a configurable bridge channel. Runs as a separate process with its own DOMCA-themed dashboard, YAML configuration, loop prevention and systemd service installer. Requires zero changes to meshcore_gui. See 11. Cross-Frequency Bridge for details
3. Screenshots
4. Requirements
- Python 3.10+
- Serial mode: USB serial connection + Serial Companion firmware on the device
- BLE mode: Bluetooth adapter + Linux with BlueZ (D-Bus); additional Python packages:
bleak,dbus_fast
4.1. Platform Support
| Platform | Serial | BLE | Status |
|---|---|---|---|
| Linux (Ubuntu/Debian) | ✅ pySerial | ✅ bleak + dbus_fast | ✅ Tested |
| Raspberry Pi 5 (Debian Bookworm) | ✅ pySerial | ✅ bleak + dbus_fast | ✅ Tested (headless) |
| macOS | ✅ pySerial | ❌ No D-Bus | ⬜ Serial untested |
| Windows 10/11 | ✅ pySerial | ❌ No D-Bus | ⬜ Serial untested |
5. Installation
5.1. System Dependencies
Linux (Ubuntu/Debian) — Serial:
sudo apt update
sudo apt install python3-pip python3-venv
Linux (Ubuntu/Debian) — BLE (additionally):
sudo apt install bluetooth bluez
Verify that the Bluetooth service is running:
sudo systemctl status bluetooth
⚠️ BlueZ driver warning: Recent versions of BlueZ (5.66+, shipped with Ubuntu 24.04 and Debian Bookworm) introduced changes to the BLE connection handling and D-Bus agent API that can cause connection instability, pairing failures and unexpected disconnects in BLE mode. Known symptoms include:
- Pairing succeeds but the connection drops within seconds
org.bluez.Error.AuthenticationFailedororg.bluez.Error.ConnectionAttemptFailedin the logs- Repeated bond/unbond cycles without a stable connection
Workaround: If you experience BLE instability, try downgrading BlueZ to 5.65 or pinning the package version. Alternatively, use USB serial mode which is not affected by BlueZ and provides the most reliable connection on all platforms. See 13.1.2. BLE Quick Fixes for troubleshooting steps.
Raspberry Pi (Raspberry Pi OS Lite) — Serial:
sudo apt update
sudo apt install python3-pip python3-venv git
Raspberry Pi — BLE (additionally):
sudo apt install bluetooth bluez
The Raspberry Pi 5 has a built-in Bluetooth adapter. Verify with hciconfig or bluetoothctl show.
⚠️ Raspberry Pi OS Bookworm ships with BlueZ 5.66+ which is affected by the BLE stability issues described above. If BLE connections are unreliable, consider USB serial as the primary transport.
macOS:
# Python 3.10+ via Homebrew (if not already installed)
brew install python
No additional system packages needed. BLE mode is not supported on macOS (requires Linux D-Bus).
Windows:
- Install Python 3.10+ (check "Add to PATH" during installation)
- No additional system packages needed. BLE mode is not supported on Windows (requires Linux D-Bus).
5.1.1. D-Bus Policy for BLE (Linux only)
BLE mode uses a D-Bus PIN agent to handle automatic pairing. Your user needs permission to interact with BlueZ over the system bus. Create a policy file:
sudo tee /etc/dbus-1/system.d/meshcore-ble.conf > /dev/null << 'EOF'
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<policy user="YOUR_USERNAME">
<allow send_destination="org.bluez"/>
<allow send_interface="org.bluez.Agent1"/>
<allow send_interface="org.bluez.AgentManager1"/>
</policy>
</busconfig>
EOF
Replace YOUR_USERNAME with your actual username. This step is handled automatically if you use the install_ble_stable.sh installer (see 7.5.1).
Note: Without this policy, the BLE PIN agent cannot register with BlueZ and pairing will fail with a D-Bus permission error.
5.2. Clone the Repository
git clone https://github.com/pe1hvh/meshcore-gui.git
cd meshcore-gui
5.3. Create Virtual Environment
Linux / macOS:
python3 -m venv venv
source venv/bin/activate
Windows:
python -m venv venv
venv\Scripts\activate
5.4. Install Python Packages
Core (Serial mode):
pip install nicegui meshcore meshcoredecoder
BLE mode (additionally):
pip install bleak dbus_fast
Note: BLE dependencies (
bleak,dbus_fast) are only needed when connecting via Bluetooth LE. Serial-only installs do not require them — they are imported lazily at runtime.
6. Usage
6.1. Activate the Virtual Environment
Linux / macOS:
cd meshcore-gui
source venv/bin/activate
Windows:
cd meshcore-gui
venv\Scripts\activate
6.2. Find Your Device
Serial — Linux:
ls -l /dev/serial/by-id
Look for your MeshCore device and note the device path (e.g., /dev/ttyUSB0).
Serial — macOS:
ls /dev/tty.usb* /dev/tty.usbserial* /dev/tty.usbmodem*
Serial — Windows:
Open Device Manager → Ports (COM & LPT) and note the COM port (e.g., COM3).
BLE — Linux:
bluetoothctl scan on
Look for your MeshCore device and note the MAC address (e.g., AA:BB:CC:DD:EE:FF).
6.3. Configure Channels (optional)
Channels are automatically discovered from the device at startup via the serial link. No manual configuration is required.
If you want to cache the discovered channel list to disk (for faster startup), set CHANNEL_CACHE_ENABLED = True in meshcore_gui/config.py. By default, channels are always fetched fresh from the device.
Note: The maximum number of channel slots probed can be adjusted via
MAX_CHANNELSinconfig.py(default: 8, which matches the MeshCore protocol limit).
6.4. Start the GUI
See 7. Starting the Application below for all startup methods.
7. Starting the Application
MeshCore GUI is a web-based application powered by NiceGUI. Once started, it serves a dashboard that you can access from any browser — locally or over your network. There are several ways to run it, depending on your use case.
All examples below assume you have activated the virtual environment and are in the project directory:
cd ~/meshcore-gui
source venv/bin/activate # Linux / macOS
7.1. Command-Line Options
The transport mode is auto-detected from the device argument:
- Path like
/dev/ttyUSB0orCOM3→ Serial mode - MAC address like
literal:AA:BB:CC:DD:EE:FF→ BLE mode
| Flag | Description | Default | Mode |
|---|---|---|---|
--debug-on |
Enable verbose debug logging (stdout + log file) | Off | Both |
--port=PORT |
Web server port | 8081 |
Both |
--ssl |
Enable HTTPS with auto-generated certificate | Off | Both |
--baud=BAUD |
Serial baudrate | 115200 |
Serial |
--serial-cx-dly=SECONDS |
Serial connection delay | 0.1 |
Serial |
--ble-pin PIN |
BLE pairing PIN | 123456 |
BLE |
All flags are optional and can be combined in any order:
# Serial
python meshcore_gui.py /dev/ttyUSB0 --debug-on --port=8082 --baud=115200
# BLE
python meshcore_gui.py literal:AA:BB:CC:DD:EE:FF --debug-on --ble-pin 654321
7.2. Method 1: Interactive (foreground)
The simplest way to start — runs in your current terminal. Output is visible directly. Press Ctrl+C to stop.
Serial:
python meshcore_gui.py /dev/ttyUSB0
BLE:
python meshcore_gui.py literal:AA:BB:CC:DD:EE:FF
Open your browser at http://localhost:8081 (or the port you specified with --port).
This is the recommended method during development or when debugging, because you see all output immediately in your terminal.
7.3. Method 2: Background with Visible Output (nohup + tail)
Runs in the background but keeps the output visible in your terminal. Useful for SSH sessions where you want to monitor the application while keeping the terminal usable.
Serial:
nohup python meshcore_gui.py /dev/ttyUSB0 --debug-on > ~/meshcore.log 2>&1 &
tail -f ~/meshcore.log
BLE:
nohup python meshcore_gui.py literal:AA:BB:CC:DD:EE:FF --debug-on > ~/meshcore.log 2>&1 &
tail -f ~/meshcore.log
The first command starts the application in the background and writes all output to ~/meshcore.log. The & at the end returns control to your terminal. The second command follows the log file in real-time — press Ctrl+C to stop following (the application keeps running).
7.4. Method 3: Background with Terminal Free (nohup)
Runs entirely in the background. Your terminal is free and the application survives closing your SSH session. Ideal for headless devices where you start the application once and leave it running.
Serial:
nohup python meshcore_gui.py /dev/ttyUSB0 --debug-on > ~/meshcore.log 2>&1 &
BLE:
nohup python meshcore_gui.py literal:AA:BB:CC:DD:EE:FF --debug-on > ~/meshcore.log 2>&1 &
To check if it is running:
ps aux | grep meshcore_gui
To view recent output:
tail -50 ~/meshcore.log
To stop it:
pkill -f meshcore_gui
Tip: Avoid redirecting to
/dev/null— keeping the output in a log file preserves connection errors and other diagnostics. When--debug-onis enabled, detailed debug output is also written to a per-device rotating log file at~/.meshcore-gui/logs/<ADDRESS>_meshcore_gui.log(e.g.F0_9E_9E_75_A3_01_meshcore_gui.log, max 20 MB, rotates automatically).
7.5. Method 4: systemd Service (recommended for production)
A systemd service starts automatically on boot, restarts on crashes, and integrates with system logging. This is the recommended method for permanent headless deployments (e.g. Raspberry Pi).
7.5.1. Automated Setup
Use the appropriate installer for your transport:
# Serial connection
bash install_serial.sh
# BLE connection
bash install_ble_stable.sh
Serial environment variables (optional):
SERIAL_PORT=/dev/ttyACM0
BAUD=115200
SERIAL_CX_DLY=0.1
WEB_PORT=8081
DEBUG_ON=yes
BLE environment variables (optional):
BLE_ADDRESS=AA:BB:CC:DD:EE:FF
WEB_PORT=8081
DEBUG_ON=yes
The BLE installer also installs the D-Bus policy file and configures the systemd service with the correct DBUS_SYSTEM_BUS_ADDRESS environment variable.
7.5.2. Manual Setup
Serial
Step 1 — Create the service file:
sudo nano /etc/systemd/system/meshcore-gui.service
[Unit]
Description=MeshCore GUI (Serial)
[Service]
Type=simple
User=your-username
WorkingDirectory=/home/your-username/meshcore-gui
ExecStart=/home/your-username/meshcore-gui/venv/bin/python meshcore_gui.py /dev/ttyUSB0 --debug-on --port=8081 --baud=115200
Restart=on-failure
RestartSec=30
[Install]
WantedBy=multi-user.target
Replace your-username, /dev/ttyUSB0 and port with your actual values.
BLE
Step 1 — Ensure the D-Bus policy is installed (see 5.1.1).
Step 2 — Create the service file:
sudo nano /etc/systemd/system/meshcore-gui.service
[Unit]
Description=MeshCore GUI (BLE)
After=bluetooth.target
Wants=bluetooth.target
[Service]
Type=simple
User=your-username
WorkingDirectory=/home/your-username/meshcore-gui
ExecStart=/home/your-username/meshcore-gui/venv/bin/python meshcore_gui.py literal:AA:BB:CC:DD:EE:FF --debug-on --port=8081 --ble-pin 123456
Restart=on-failure
RestartSec=30
Environment=DBUS_SYSTEM_BUS_ADDRESS=unix:path=/var/run/dbus/system_bus_socket
[Install]
WantedBy=multi-user.target
Replace your-username, AA:BB:CC:DD:EE:FF and PIN with your actual values. The DBUS_SYSTEM_BUS_ADDRESS environment variable is required for the BLE PIN agent to communicate with BlueZ.
Enable and start
For both serial and BLE:
sudo systemctl daemon-reload
sudo systemctl enable meshcore-gui
sudo systemctl start meshcore-gui
Useful service commands:
| Command | Description |
|---|---|
sudo systemctl status meshcore-gui |
Check if the service is running |
sudo journalctl -u meshcore-gui -f |
Follow the live log output |
sudo journalctl -u meshcore-gui --since "1 hour ago" |
View recent logs |
sudo systemctl restart meshcore-gui |
Restart after a configuration change |
sudo systemctl stop meshcore-gui |
Stop the service |
sudo systemctl disable meshcore-gui |
Prevent starting on boot |
7.6. Accessing the Interface
Once the application is running (via any method), open a browser and navigate to:
http://localhost:8081
From another device on the same network, use the hostname or IP address:
http://<hostname-or-ip>:8081
For example: http://raspberrypi5nas:8081 or http://192.168.2.234:8081. This works from any device on the same network — desktop, laptop, tablet or phone.
7.7. Running Multiple Instances
You can run multiple instances simultaneously (e.g. for different MeshCore devices) by assigning each a different port:
# Two serial devices
python meshcore_gui.py /dev/ttyUSB0 --port=8081 --baud=115200 &
python meshcore_gui.py /dev/ttyUSB1 --port=8082 --baud=115200 &
# Mixed: serial + BLE
python meshcore_gui.py /dev/ttyACM0 --port=8081 &
python meshcore_gui.py literal:AA:BB:CC:DD:EE:FF --port=8082 &
Each instance gets its own log file, cache and archive, all keyed by the device identifier (serial port or BLE address).
7.8. Migrating Existing Data
If you are moving from an existing installation, copy the data directory to preserve your cache, pinned contacts, room server passwords and message archive:
scp -r ~/.meshcore-gui user@headless-device:~/
7.9. Raspberry Pi 5 Notes
The Raspberry Pi 5 is a good fit for running MeshCore GUI headless:
- Serial: USB serial adapter or direct USB connection to the device
- BLE: Built-in Bluetooth adapter; works out of the box with BlueZ on Raspberry Pi OS
- RAM: 2 GB is sufficient; 4 GB or more provides extra headroom for long-running operation
- OS: Raspberry Pi OS Lite (64-bit, Bookworm) — no desktop environment needed
- Storage: 16 GB+ SD card or NVMe; the application stores cache and archive data in
~/.meshcore-gui/ - Power: Low idle power consumption (~5W), suitable for 24/7 operation
Ensure your user has permission to access the serial device (e.g. member of dialout on many Linux distros).
8. Configuration
| Setting | Location | Description |
|---|---|---|
OPERATOR_CALLSIGN |
meshcore_gui/config.py |
Operator callsign shown on landing page and drawer footer (default: "PE1HVH") |
LANDING_SVG_PATH |
meshcore_gui/config.py |
Path to the landing page SVG file; supports {callsign} placeholder (default: static/landing_default.svg) |
DEBUG |
meshcore_gui/config.py |
Set to True for verbose logging (or use --debug-on) |
MAX_CHANNELS |
meshcore_gui/config.py |
Maximum channel slots to probe on device (default: 8) |
CHANNEL_CACHE_ENABLED |
meshcore_gui/config.py |
Cache discovered channels to disk for faster startup (default: False — always fresh from device) |
DEFAULT_TIMEOUT |
meshcore_gui/config.py |
Default command timeout in seconds (default: 10.0) |
MESHCORE_LIB_DEBUG |
meshcore_gui/config.py |
Enable meshcore library debug logging (default: True) |
SERIAL_BAUDRATE |
meshcore_gui/config.py |
Serial baudrate (default: 115200) |
SERIAL_CX_DELAY |
meshcore_gui/config.py |
Serial connection delay (default: 0.1) |
TRANSPORT |
meshcore_gui/config.py |
Auto-detected transport mode: "serial" or "ble" (set at startup) |
BLE_PIN |
meshcore_gui/config.py |
BLE pairing PIN for T1000e devices (default: "123456") |
RECONNECT_MAX_RETRIES |
meshcore_gui/config.py |
Maximum reconnect attempts after a disconnect (default: 5) |
RECONNECT_BASE_DELAY |
meshcore_gui/config.py |
Base delay in seconds between reconnect attempts, multiplied by attempt number (default: 5.0) |
CONTACT_REFRESH_SECONDS |
meshcore_gui/config.py |
Interval between periodic contact refreshes (default: 300s / 5 minutes) |
MESSAGE_RETENTION_DAYS |
meshcore_gui/config.py |
Retention period for archived messages (default: 30 days) |
RXLOG_RETENTION_DAYS |
meshcore_gui/config.py |
Retention period for archived RX log entries (default: 7 days) |
CONTACT_RETENTION_DAYS |
meshcore_gui/config.py |
Retention period for cached contacts (default: 90 days) |
KEY_RETRY_INTERVAL |
meshcore_gui/ble/worker.py |
Interval between background retry attempts for missing channel keys (default: 30s) |
BOT_DEVICE_NAME |
meshcore_gui/config.py |
Device name set when bot mode is active (default: ;NL-OV-ZWL-STDSHGN-WKC Bot) |
BOT_CHANNELS |
meshcore_gui/services/bot.py |
Channel indices the bot listens on |
BOT_COOLDOWN_SECONDS |
meshcore_gui/services/bot.py |
Minimum seconds between bot replies |
BOT_KEYWORDS |
meshcore_gui/services/bot.py |
Keyword → reply template mapping |
| Room passwords | ~/.meshcore-gui/room_passwords/<ADDRESS>.json |
Per-device Room Server passwords (managed via GUI, stored outside repository) |
| Serial Port | CLI argument | Device serial port (e.g. /dev/ttyUSB0 or COM3) |
| BLE Address | CLI argument | BLE MAC address (e.g. literal:AA:BB:CC:DD:EE:FF) |
--port=PORT |
CLI flag | Web server port (default: 8081) |
--baud=BAUD |
CLI flag | Serial baudrate (default: 115200) |
--serial-cx-dly=SECONDS |
CLI flag | Serial connection delay (default: 0.1) |
--ble-pin PIN |
CLI flag | BLE pairing PIN (default: 123456) |
--ssl |
CLI flag | Enable HTTPS with auto-generated self-signed certificate |
--debug-on |
CLI flag | Enable verbose debug logging |
9. Functionality
9.1. Device Info
- Name, frequency, SF/BW, TX power, location, firmware version
9.2. Contacts
- List of known nodes with type and location
- Click on a contact to send a DM (or add a Room Server panel for type=3 contacts)
- Pin/Unpin: Checkbox per contact to pin it — pinned contacts are sorted to the top and visually marked with a yellow background. Pin state is persisted locally and survives app restart.
- Individual delete: 🗑️ button per unpinned contact to remove a single contact from the device with confirmation dialog. Pinned contacts are protected.
- Bulk delete: "🧹 Clean up" button removes all unpinned contacts from the device in one action, with a confirmation dialog showing how many will be removed vs. kept. Optional "Also delete from history" checkbox to clear locally cached data.
- Auto-add toggle: "📥 Auto-add" checkbox controls whether the device automatically adds new contacts when it receives adverts from other mesh nodes. Disabled by default to prevent the contact list from filling up.
9.3. Map
- OpenStreetMap with markers for own position and contacts
- Shows your own position (blue marker)
- Automatically centers on your own position
9.4. Channel Messages
- Select a channel in the dropdown
- Type your message and click "Send"
- Received messages appear in the messages list
- Filter messages via the checkboxes
9.5. Direct Messages (DM)
- Click on a contact in the contacts list
- A dialog opens where you can type your message
- Click "Send" to send the DM
9.6. Message Route Visualization
Click on any message in the messages list to open a route page in a new tab. The route page shows:
- Hop summary — Number of hops and SNR
- Interactive map — Leaflet map with markers for sender, repeaters and receiver, connected by a polyline showing the message path
- Route table — Detailed table with each hop: name, ID (first byte of public key), node type and GPS coordinates
- Reply panel — Pre-filled reply message with route acknowledgement (sender, path length, repeater IDs)
Route data is resolved from two sources (in priority order):
- RX log packet decode — Path hashes extracted from the raw LoRa packet via
meshcoredecoder - Contact out_path — Stored route from the sender's contact record (fallback)
Route table data (path hashes, resolved repeater names and channel names) is captured at receive time and stored in the archive. This means route tables (names and IDs) remain correct even when contacts are renamed, removed or offline. Sender identity is resolved via pubkey lookup with an automatic name-based fallback when the pubkey lookup fails. Map visualization still depends on live contact GPS data — see 12. Known Limitations.
9.7. Room Server
Room Servers (type=3 contacts) allow group-style messaging via a shared server node in the mesh network.
Adding a Room Server: Click on any Room Server contact (🏠 icon) in the contacts list. A dialog opens where you enter the room password. Click "Add & Login" to create a dedicated room panel and log in.
Room panel features:
- Each Room Server gets its own card in the centre column below the Messages panel
- After login: the password field is replaced by a Logout button
- Messages from the room are displayed in the card with correct author attribution (the real sender, not the room server)
- Send messages to the room via the input field and Send button
- Room panels are restored from stored passwords on app restart
How it works under the hood:
- Login via
send_login(pubkey, password)— the Room Server authenticates and starts pushing messages over LoRa RF - Messages arrive asynchronously via
MESSAGES_WAITINGevents (event-driven, no polling) - Room messages use
txt_type=2(signed), where thesignaturefield contains the 4-byte pubkey prefix of the real author - The first message may take 10–75 seconds to arrive after login (inherent LoRa RF latency)
- Passwords are stored in
~/.meshcore-gui/room_passwords/outside the repository
Note: The Room Server pushes messages round-robin to all logged-in clients. With many clients or large message buffers, it can take several minutes to receive all historical messages.
9.8. Message Archive
All incoming messages and RX log entries are automatically persisted to disk in ~/.meshcore-gui/archive/. One JSON file per data type per device identifier.
Click the 📚 Archive button in the Messages panel header to open the archive viewer in a new tab. The archive viewer provides:
- Pagination — 50 messages per page, with Previous/Next navigation
- Channel filter — Filter by specific channel or view all
- Time range filter — Last 24 hours, 7 days, 30 days, 90 days, or all time
- Text search — Case-insensitive search in message text
- Inline route tables — Expandable route display per message (sender, repeaters, receiver with names and IDs)
- Reply from archive — Expandable reply panel per message with pre-filled @sender mention
Old data is automatically cleaned up based on configurable retention periods (MESSAGE_RETENTION_DAYS, RXLOG_RETENTION_DAYS in config.py).
9.9. Local Cache
Device info, contacts and channel keys are automatically cached to disk in ~/.meshcore-gui/cache/. One JSON file is created per device identifier.
Startup behaviour:
- Cache is loaded first — GUI is immediately populated with the last known state
- Connection is established in the background (serial or BLE)
- Fresh data from the device updates both the GUI and the cache
Channel key loading:
Channel key loading uses a cache-first strategy with device fallback:
- Cached keys are loaded first and never overwritten by name-derived fallbacks
- Each channel is queried from the device at startup
- Channels that fail are retried in the background every 30 seconds
- Successfully loaded keys are immediately written to the cache for next startup
Contact merge strategy:
- New contacts from the device are added to the cache with a
last_seentimestamp - Existing contacts are updated (fresh data wins)
- Contacts only in cache (node offline) are preserved
If the connection fails (serial or BLE), the GUI remains usable with cached data and shows an offline status.
9.10. Keyword Bot
The built-in bot automatically replies to messages containing recognised keywords. Enable or disable it via the 🤖 BOT checkbox in the filter bar.
Device name switching: When the BOT checkbox is enabled, the device name is automatically changed to the configured BOT_DEVICE_NAME (default: ;NL-OV-ZWL-STDSHGN-WKC Bot). The original device name is saved and restored when bot mode is disabled. This allows the mesh network to identify the node as a bot by its name.
Default keywords:
| Keyword | Reply |
|---|---|
test |
<sender>, rcvd | SNR <snr> | path(<hops>); <repeaters> |
ping |
Pong! |
help |
test, ping, help |
Safety guards:
- Only replies on configured channels (
BOT_CHANNELS) - Ignores own messages and messages from other bots (names ending in "Bot")
- Cooldown period between replies (default: 5 seconds)
Customisation: Edit BOT_KEYWORDS in meshcore_gui/services/bot.py. Templates support {sender}, {snr} and {path} variables.
9.11. RX Log
- Received packets with SNR and type
9.12. Actions
- Refresh data
- Send advertisement
10. Architecture
┌─────────────────┐ ┌─────────────────┐
│ Main Thread │ │ Worker Thread │
│ (NiceGUI) │ │ (asyncio) │
│ │ │ │
│ ┌───────────┐ │ │ ┌───────────┐ │
│ │ Dashboard │◄─┼──┬──┼─►│ Worker │ │
│ └───────────┘ │ │ │ │ (Serial │ │
│ │ │ │ │ │ or BLE) │ │
│ ▼ │ │ │ └─────┬─────┘ │
│ ┌───────────┐ │ │ │ │Commands │ │
│ │ Timer │ │ │ │ │Events │ │
│ │ (500ms) │ │ │ │ │Decoder │ │
│ └───────────┘ │ │ │ └────┬────┘ │
│ │ │ │ │ │ │
│ ┌─────┴─────┐ │ │ │ ┌────┴────┐ │
│ │ Panels │ │ │ │ │ Bot │ │
│ │ RoutePage│ │ │ │ │ Dedup │ │
│ │ ArchivePg │ │ │ │ │ Cache │ │
│ │ RoomSrvPnl│ │ │ │ └─────────┘ │
│ └───────────┘ │ │ │ ┌─────────┐ │
│ │ │ │ │Reconnect│ │
│ │ │ │ │ Loop │ │
│ │ │ │ └─────────┘ │
└─────────────────┘ │ └─────────────────┘
┌──────┴──────┐
│ SharedData │ ┌───────────────┐
│ (thread- │ │ DeviceCache │
│ safe) │ │ (~/.meshcore- │
└──────┬──────┘ │ gui/cache/) │
│ └───────────────┘
┌──────┴──────┐ ┌───────────────┐
│ Message │ │ PinStore │
│ Archive │ │ Contact │
│ (~/.meshcore│ │ Cleaner │
│ -gui/ │ │ RoomPassword │
│ archive/) │ │ Store │
└─────────────┘ └───────────────┘
- Worker (Serial/BLE): Runs in separate thread with its own asyncio loop. Auto-detected transport:
SerialWorkerfor USB serial,BLEWorkerfor Bluetooth LE (with PIN agent and bond management). Both share a common base class with disconnect detection, auto-reconnect and background key retry - CommandHandler: Executes commands (send message, advert, refresh, purge unpinned, set auto-add, set bot name, restore name, login room, send room msg, remove single contact)
- EventHandler: Processes incoming device events (messages, RX log) with path hash caching between RX_LOG and fallback handlers, and resolves repeater names at receive time for self-contained archive data
- PacketDecoder: Decodes raw LoRa packets and extracts route data
- MeshBot: Keyword-triggered auto-reply on configured channels with automatic device name switching
- DualDeduplicator: Prevents duplicate messages (hash-based + content-based)
- DeviceCache: Local JSON cache per device for instant startup and offline resilience
- MessageArchive: Persistent storage for messages and RX log with configurable retention and automatic cleanup
- PinStore: Persistent pin state storage per device (JSON-backed)
- ContactCleanerService: Bulk-delete logic for unpinned contacts with statistics
- RoomServerPanel: Per-room-server card management with login/logout, message display and send functionality
- RoomPasswordStore: Persistent Room Server password storage per device in
~/.meshcore-gui/room_passwords/(JSON-backed, analogous to PinStore) - SharedData: Thread-safe data sharing between serial worker and GUI via Protocol interfaces
- DashboardPage: Main GUI with modular panels (device, contacts, map, messages, etc.)
- RoutePage: Standalone route visualization page opened per message
- ArchivePage: Archive viewer with filters, pagination and inline route tables
- Communication: Via command queue (GUI→worker) and shared state with flags (worker→GUI)
11. Cross-Frequency Bridge
11.1. Bridge Overview
meshcore_bridge is a standalone daemon that connects two MeshCore devices operating on different radio frequencies. It forwards messages on a configurable bridge channel from one device to the other, effectively extending your mesh network across frequency boundaries.
The bridge runs as an independent process, imports the existing meshcore_gui modules (SharedData, Worker, models, config) as a library, and requires zero modifications to the meshcore_gui codebase.
┌───────────────────────────────────────────┐
│ meshcore_bridge daemon │
│ │
│ ┌──────────────┐ ┌────────────────┐ │
│ │ SharedData A │ │ BridgeEngine │ │
│ │ + Worker A │◄──►│ (forward & │ │
│ │ (ttyUSB1) │ │ dedup) │ │
│ └──────────────┘ └────────────────┘ │
│ ┌──────────────┐ │ │
│ │ SharedData B │◄────────┘ │
│ │ + Worker B │ │
│ │ (ttyUSB2) │ │
│ └──────────────┘ │
│ │
│ ┌───────────────────────────────────┐ │
│ │ Bridge Dashboard (NiceGUI :9092) │ │
│ └───────────────────────────────────┘ │
└───────────────────────────────────────────┘
Key properties:
- Separate process — the bridge runs independently from meshcore_gui; both can run simultaneously on the same host
- Loop prevention — three mechanisms prevent message loops: direction filter, message hash tracking, and echo suppression
- Private channels — encrypted channels work transparently because the bridge operates at the plaintext level between firmware decryption and encryption
- DOMCA dashboard — status page on its own port showing both device connections, bridge statistics and a forwarded message log
- YAML configuration — all settings in a single
bridge_config.yamlfile
11.2. Quick Start
# 1. Install the additional dependency
pip install pyyaml
# 2. Edit the configuration
cp bridge_config.yaml bridge_config.yaml.local
nano bridge_config.yaml.local
# 3. Start the bridge
python meshcore_bridge.py --config=bridge_config.yaml.local
# 4. Open the dashboard at http://localhost:9092
Prerequisites: two MeshCore devices connected via USB serial to the same host, with the bridge channel configured on both devices using the same channel secret/password.
11.3. Bridge Configuration
All settings are defined in bridge_config.yaml:
bridge:
channel_name: "bridge" # Channel name (for display)
channel_idx_a: 3 # Channel index on device A
channel_idx_b: 3 # Channel index on device B
poll_interval_ms: 200 # Polling interval (ms)
forward_prefix: true # Add [sender] prefix to forwarded messages
max_forwarded_cache: 500 # Loop prevention cache size
device_a:
port: /dev/ttyUSB1
baud: 115200
label: "869.525 MHz"
device_b:
port: /dev/ttyUSB2
baud: 115200
label: "868.000 MHz"
gui:
port: 9092
title: "MeshCore Bridge"
CLI options: --config=PATH, --port=PORT, --debug-on, --help.
11.4. systemd Service
Install the bridge as a systemd daemon for production use:
sudo bash install_bridge.sh
sudo nano /etc/meshcore/bridge_config.yaml
sudo systemctl start meshcore-bridge
sudo systemctl enable meshcore-bridge
To uninstall: sudo bash install_bridge.sh --uninstall
For full documentation including architecture details, troubleshooting and assumptions, see BRIDGE.md.
12. Known Limitations
- Channel discovery timing — Dynamic channel discovery probes the device at startup; on very slow links (especially BLE), some channels may be missed on first attempt. Channels are retried in the background and cached for subsequent startups when
CHANNEL_CACHE_ENABLED = True - Initial load time — GUI waits for device data before the first render is complete (mitigated by cache: if cached data exists, the GUI populates instantly)
- Archive route map visualization — Route table names and IDs are now stored at receive time and display correctly regardless of current contacts. However, the route map still depends on GPS coordinates from contacts currently in memory; archived messages without recent contact data may show incomplete map markers
- Room Server message latency — Room Server messages travel over LoRa RF and arrive asynchronously (10–75 seconds per message). With many logged-in clients, receiving all historical messages can take 10+ minutes due to the round-robin push protocol
- BLE Linux only — BLE mode requires Linux with BlueZ and D-Bus. macOS and Windows are not supported for BLE connections because the PIN agent relies on the D-Bus system bus
- BlueZ 5.66+ instability — Recent BlueZ versions (shipped with Ubuntu 24.04, Debian Bookworm, Raspberry Pi OS Bookworm) can cause BLE connection instability, pairing failures and unexpected disconnects. USB serial is not affected and is recommended as the most reliable transport
13. Troubleshooting
14.1. Linux
For Linux troubleshooting, start by checking device permissions and that the correct device argument is used.
13.1.1. Serial Quick Fixes
GUI remains empty / serial connection fails
- Check the service logs:
journalctl -u meshcore-gui -n 50 --no-pager - Confirm the serial device exists and is readable:
ls -l /dev/serial/by-id - Ensure your user has serial permissions (commonly
dialouton Linux):sudo usermod -a -G dialout $USER # Log out and back in - Kill any existing GUI instance and free the port:
pkill -9 -f meshcore_gui sleep 3 - Restart the GUI:
python meshcore_gui.py /dev/ttyUSB0
13.1.2. BLE Quick Fixes
GUI remains empty / BLE connection fails
-
Verify Bluetooth is running:
sudo systemctl status bluetoothIf not running:
sudo systemctl start bluetooth -
Check that the device is visible:
bluetoothctl scan onLook for your device's MAC address. Press
Ctrl+Cto stop scanning. -
Verify the D-Bus policy is installed:
ls -l /etc/dbus-1/system.d/meshcore-ble.confIf missing, see 5.1.1. D-Bus Policy for BLE.
-
Remove stale BLE bond (if the device was previously paired):
bluetoothctl remove AA:BB:CC:DD:EE:FF -
Kill any existing GUI instance:
pkill -9 -f meshcore_gui sleep 3 -
Restart the GUI:
python meshcore_gui.py literal:AA:BB:CC:DD:EE:FF --debug-onCheck the debug output for D-Bus or pairing errors.
BLE PIN agent errors
If you see org.freedesktop.DBus.Error.AccessDenied in the logs, the D-Bus policy is missing or incorrect. Reinstall it per 5.1.1 and reload D-Bus:
sudo systemctl reload dbus
BLE reconnect issues
If the connection drops and does not recover, the BLE bond may be stale. The application includes automatic bond cleanup and reconnect logic, but in some cases a manual bond removal is needed:
bluetoothctl remove AA:BB:CC:DD:EE:FF
sudo systemctl restart meshcore-gui
BlueZ 5.66+ driver instability
If BLE connections are consistently unreliable (frequent disconnects, pairing loops, authentication errors), the issue is likely caused by changes in BlueZ 5.66+. Check your BlueZ version:
bluetoothctl --version
If the version is 5.66 or higher, you have several options:
-
Switch to USB serial (recommended) — the most reliable workaround. Connect your device via USB and use serial mode instead:
python meshcore_gui.py /dev/ttyACM0 -
Downgrade BlueZ — on Debian/Ubuntu, you can pin an older version:
sudo apt install bluez=5.65-0ubuntu1 sudo apt-mark hold bluezNote: exact package versions vary by distribution.
-
Disable LE Privacy and Secure Connections — in some cases, adding these options to
/etc/bluetooth/main.confcan help:[General] Privacy = off [LE] MinConnectionInterval=6 MaxConnectionInterval=9 ConnectionLatency=0Restart Bluetooth after editing:
sudo systemctl restart bluetooth
14.2. macOS
- Ensure the device shows up under
/dev/tty.usb*,/dev/tty.usbserial*, or/dev/tty.usbmodem* - Close any other app that might be using the serial port
13.3. Windows
- Confirm the COM port in Device Manager → Ports (COM & LPT)
- Close any other app that might be using the COM port
13.4. All Platforms
13.4.1. Device Not Found
Serial: Make sure the MeshCore device is powered on, running Serial Companion firmware, and the correct serial port is selected.
BLE: Ensure the device is powered on and discoverable (bluetoothctl scan on). Check that the MAC address is correct and that the BLE PIN matches (default: 123456). On Linux, verify D-Bus permissions — see docs/ble/BLE_ARCHITECTURE.md for details.
13.4.2. Messages Not Arriving
- Check if your channels are correctly configured
- Use
meshclito verify that messages are arriving
13.4.3. Clearing the Cache
If cached data causes issues (e.g. stale contacts), delete the cache file:
rm ~/.meshcore-gui/cache/*.json
The cache will be recreated on the next successful serial connection.
14. Development
14.1. Debug Mode
Enable via command line flag:
python meshcore_gui.py /dev/ttyUSB0 --debug-on
Or set DEBUG = True in meshcore_gui/config.py.
Debug output is written to both stdout and a per-device rotating log file at ~/.meshcore-gui/logs/<ADDRESS>_meshcore_gui.log (e.g. F0_9E_9E_75_A3_01_meshcore_gui.log).
14.2. Project Structure
meshcore-gui/
├── meshcore_gui.py # Entry point (auto-detects Serial or BLE)
├── install_ble_stable.sh # BLE installer (systemd service for BLE connections)
├── install_serial.sh # Serial installer (systemd service for serial connections)
├── meshcore_gui/ # Application package
│ ├── __init__.py
│ ├── __main__.py # Alternative entry: python -m meshcore_gui
│ ├── config.py # OPERATOR_CALLSIGN, LANDING_SVG_PATH, DEBUG flag, channel discovery settings (MAX_CHANNELS, CHANNEL_CACHE_ENABLED), SERIAL_* defaults, BLE_PIN, TRANSPORT mode, RECONNECT_* settings, refresh interval, retention settings, BOT_DEVICE_NAME, per-device log file naming
│ ├── ble/ # Connection layer (serial + BLE transport)
│ │ ├── __init__.py
│ │ ├── worker.py # _BaseWorker + SerialWorker + BLEWorker + create_worker() factory; thread lifecycle, cache-first startup, disconnect detection, auto-reconnect, background key retry
│ │ ├── ble_agent.py # BlueZ D-Bus PIN agent for BLE pairing (Linux only, lazy-loaded)
│ │ ├── ble_reconnect.py # BLE bond cleanup and reconnect loop via D-Bus (lazy-loaded)
│ │ ├── commands.py # Command execution (send, refresh, advert)
│ │ ├── events.py # Event callbacks (messages, RX log) with path hash caching and name resolution at receive time
│ │ └── packet_decoder.py # Raw LoRa packet decoding via meshcoredecoder
│ ├── core/ # Domain models and shared state
│ │ ├── __init__.py
│ │ ├── models.py # Dataclasses: Message, Contact, DeviceInfo, RxLogEntry, RouteNode
│ │ ├── shared_data.py # Thread-safe shared data store
│ │ └── protocols.py # Protocol interfaces (ISP/DIP)
│ ├── gui/ # NiceGUI web interface
│ │ ├── __init__.py
│ │ ├── constants.py # UI display constants
│ │ ├── dashboard.py # Main dashboard page orchestrator, loads landing SVG from config.LANDING_SVG_PATH
│ │ ├── route_page.py # Message route visualization page
│ │ ├── archive_page.py # Message archive viewer with filters and pagination
│ │ └── panels/ # Modular UI panels
│ │ ├── __init__.py
│ │ ├── device_panel.py # Device info display
│ │ ├── contacts_panel.py # Contacts list with DM, pin/unpin, bulk delete, auto-add toggle
│ │ ├── map_panel.py # Leaflet map
│ │ ├── input_panel.py # Message input and channel select
│ │ ├── filter_panel.py # Channel filters and bot toggle
│ │ ├── messages_panel.py # Filtered message display with archive button
│ │ ├── actions_panel.py # Refresh and advert buttons
│ │ ├── room_server_panel.py # Per-room-server card with login/logout and messages
│ │ └── rxlog_panel.py # RX log table
│ └── services/ # Business logic
│ ├── __init__.py
│ ├── bot.py # Keyword-triggered auto-reply bot
│ ├── cache.py # Local JSON cache per device
│ ├── contact_cleaner.py # Bulk-delete logic for unpinned contacts
│ ├── dedup.py # Message deduplication
│ ├── message_archive.py # Persistent message and RX log archive
│ ├── pin_store.py # Persistent pin state storage per device
│ ├── room_password_store.py # Persistent Room Server password storage per device
│ └── route_builder.py # Route data construction
├── docs/
│ ├── TROUBLESHOOTING.md # BLE troubleshooting guide (detailed)
│ ├── MeshCore_GUI_Design.docx # Design document
│ ├── ble_capture_workflow_t_1000_e_explanation.md
│ └── ble_capture_workflow_t_1000_e_uitleg.md
├── meshcore_bridge.py # Bridge entry point
├── meshcore_bridge/ # Bridge daemon package
│ ├── __init__.py
│ ├── __main__.py # CLI, dual-worker setup, NiceGUI server
│ ├── config.py # YAML config loading (BridgeConfig dataclass)
│ ├── bridge_engine.py # Core bridge logic: poll, forward, dedup, loop prevention
│ └── gui/ # Bridge dashboard (DOMCA themed)
│ ├── __init__.py
│ ├── dashboard.py # Bridge status dashboard page
│ └── panels/
│ ├── __init__.py
│ ├── status_panel.py # Device A/B connection status + statistics
│ └── log_panel.py # Forwarded message log
├── bridge_config.yaml # Bridge configuration template (YAML)
├── install_bridge.sh # Bridge systemd service installer
├── BRIDGE.md # Bridge documentation
├── .gitattributes
├── .gitignore
├── LICENSE
├── CHANGELOG.md
└── README.md
15. BBS — Bulletin Board System
MeshCore GUI includes an offline BBS that lets mesh nodes exchange structured messages by category, with optional region tagging.
Access model
The operator links one or more channels to the BBS. Anyone who sends a message on a configured BBS channel is automatically added to the whitelist. After that, they can send commands via Direct Message to the BBS node — the channel itself stays clean.
First contact: !bbs help on the configured channel
→ node sees the public key → whitelists it
After that: !p U need assistance as DM to the node
→ processed, reply sent back via DM
Anyone who has never sent a message on a configured channel is not on the whitelist and is silently ignored.
Settings
Open via the gear icon (⚙) in the BBS panel, or navigate to /bbs-settings.
BBS Settings
──────────────────────────────────────────
Channels: ☑ [1] NoodNet Zwolle
☑ [2] NoodNet Dalfsen
☐ [3] NoodNet OV
Categories: URGENT, MEDICAL, LOGISTICS, STATUS, GENERAL
Retain: 48 hours
[Save]
▶ Advanced
Regions (comma-separated)
Allowed keys (empty = auto-learned from channel activity)
- Channels — check all channels whose participants should have access to the BBS. Multiple channels can be selected.
- Categories — comma-separated list of valid category tags.
- Retain — message retention in hours (default 48).
- Advanced → Regions — optional region tags for geographic filtering.
- Advanced → Allowed keys — manual whitelist override; leave empty to rely on auto-learned keys only.
Command syntax
Short syntax
| Command | Description |
|---|---|
!p <cat> <text> |
Post a message |
!p <region> <cat> <text> |
Post with region |
!r |
Read 5 most recent messages (all categories) |
!r <cat> |
Read filtered by category |
!r <region> <cat> |
Read filtered by region and category |
Category abbreviations are computed automatically as the shortest unique prefix within the configured list. Example with URGENT, MEDICAL, LOGISTICS, STATUS, GENERAL:
U=URGENT M=MEDICAL L=LOGISTICS S=STATUS G=GENERAL
If two categories share the same leading letters (e.g. MEDICAL and MISSING), longer prefixes are calculated automatically: ME and MI. The !r (without arguments) and !bbs help replies always include the current abbreviation table.
Full syntax
| Command | Description |
|---|---|
!bbs help |
Show commands and abbreviation table |
!bbs post <category> <text> |
Post a message |
!bbs post <region> <category> <text> |
Post with region |
!bbs read |
Read 5 most recent messages |
!bbs read <category> |
Read filtered by category |
!bbs read <region> <category> |
Read filtered by region and category |
Example help reply
BBS [NoodNet Zwolle, NoodNet Dalfsen] | !p [cat] [text] | !r [cat] | U=URGENT M=MEDICAL L=LOGISTICS S=STATUS G=GENERAL
Error handling
| Situation | Reply |
|---|---|
| Unknown category | Lists valid categories and abbreviations |
| Ambiguous abbreviation | Lists all matching categories |
| Sender not on whitelist | Silent drop — no reply |
Storage
~/.meshcore-gui/bbs/bbs_messages.db — SQLite message store (WAL mode)
~/.meshcore-gui/bbs/bbs_config.json — Board configuration
16. Roadmap
This project is under active development. The most common features from the official MeshCore Companion apps are being implemented gradually. Planned additions include:
- Cross-frequency bridge — standalone daemon connecting two devices on different frequencies via configurable channel forwarding (see 11. Cross-Frequency Bridge)
- BBS — Bulletin Board System — offline message board with DM-based commands, category/region filtering and automatic abbreviations (see 15. BBS)
- Observer mode — passively monitor mesh traffic without transmitting, useful for network analysis, coverage mapping and long-term logging
- Room Server administration — authenticate as admin to manage Room Server settings and users directly from the GUI
- Repeater management — connect to repeater nodes to view status and adjust configuration
Have a feature request or want to contribute? Open an issue or submit a pull request.
16. Disclaimer
This is an independent community project and is not affiliated with or endorsed by the official MeshCore development team. It is built on top of the open-source meshcore Python library.
17. License
MIT License - see LICENSE file
18. Author
PE1HVH — GitHub
19. Acknowledgments
- MeshCore — Mesh networking firmware and protocol
- meshcore_py — Python bindings for MeshCore
- meshcore-cli — Command line interface
- meshcoredecoder — LoRa packet decoder and channel crypto
- NiceGUI — Python GUI framework