A powerful web-based communication gateway for Meshtastic radios. Features message bridging (2+ radios), AI assistant, email/Discord notifications, interactive commands, and real-time monitoring. Built with React, TypeScript, and Node.js with official Meshtastic libraries.
Meshtastic-Only Version - Focused exclusively on Meshtastic protocol support for optimal performance and reliability.
Works great with just ONE radio! While designed for bridging multiple radios, the AI assistant, email/Discord notifications, and command system make it incredibly useful even with a single device.
Note: Add your own screenshots to showcase the application! Suggested screenshots to include:
Real-time monitoring of connected radios, message traffic, and system statistics
Live feed of all mesh messages with timestamps and signal quality
Channel utilization, signal quality analysis, and network insights
SOS tracking, auto-response, and severe weather monitoring from NWS
Visualize node locations and mesh network topology
Configure local AI models with Ollama integration
Team Awareness Kit style tactical display with breadcrumb trails
NEW Architecture:
- 🌐 Web-Based GUI - accessible from any modern browser
- ⚡ Node.js Bridge Server - using official @meshtastic/core library
- 🔌 WebSocket Communication - real-time updates between frontend and bridge
- 📱 PWA Support - installable as a progressive web app
- 🔄 Auto Message Forwarding - intelligent channel-aware bridging
- ✨ Smart Channel Matching - handles different channel configurations across radios
- 🛠️ System Service Support - runs as permanent service with auto-start on boot
- 💾 Persistent Configuration - all settings saved automatically across restarts
🎯 Core Functionality
- 📡 Support for 1+ Meshtastic radios (single radio gets AI, commands, notifications!)
- 🔄 Automatic bidirectional message forwarding (2+ radios only, no manual route configuration needed)
- 🔐 Smart channel matching - forwards messages based on PSK+name, not just index
- 🛡️ Message deduplication and loop prevention
- 🔌 Auto-detect USB-connected devices
- ⚡ Real-time message forwarding and monitoring
- 🔄 Robust reconnection - automatic cleanup and self-healing after radio reboots
- 💾 Stable radio IDs - same radio always gets same ID on reconnect (no duplicates!)
⚙️ Radio Configuration
- 📻 Channel configuration via serial - get/set channel settings for all 8 channels
- 🔐 Complete channel control - name, PSK, role, uplink/downlink settings
- 📡 LoRa config display - view radio parameters directly in UI
- 🔁 Bulk operations - get all channels at once
- 💾 Persistent settings - all configurations saved automatically
📊 Monitoring & Analytics
- Real-time dashboard with live statistics
- Message traffic monitoring
- Per-radio message counters (received, forwarded)
- Connection status indicators
- Message history with filtering
- 📊 Memory management - automatic log rotation and cleanup
- 🔋 Long-running optimized - perfect for 24/7 deployments
⚙️ Advanced Channel Handling
- Cross-index forwarding - handles radios with channels on different indices
- Private channel support - forwards encrypted channels if configured on both bridge radios
- Multi-mesh bridging - can bridge between different encrypted meshes
- Automatic PSK matching - finds matching channels by encryption key and name
🤖 Interactive Command System
- Bridge commands - send commands like
#weather Seattleor#pingfrom your radio - 16 built-in commands - weather, status, uptime, radios, channels, stats, ai, email, discord, and more
- Rate limiting - prevents command spam (max 10/min per user)
- No phone needed - get info directly on radio screen
- Fully configurable - enable/disable commands, change prefix, customize
🧠 AI Assistant
- Local AI queries - ask questions via
#ai [question]or#ask [question]from your radio - Powered by Ollama - runs locally on your hardware (Raspberry Pi 4+ supported)
- Mesh-optimized - responses automatically shortened to fit Meshtastic message limits (~200 chars)
- Multiple models - choose from ultra-fast 1B models to more capable 3B models
- Model management UI - install, switch, and configure models directly from web interface
- Rate limited - max 3 AI queries per minute per user to conserve resources
- No cloud required - completely local, private, and works offline
📧 Communication Notifications
- Email notifications - send emails from your radio via
#email [message] - Discord webhook (one-way) - post to Discord channels via
#discord [message] - Discord bot (two-way) - receive Discord messages and forward to mesh!
- Multi-channel notify - send to both with
#notify [message] - SMTP support - works with Gmail, Outlook, custom mail servers
- Web UI configuration - configure and test from the interface
- Perfect for alerts - notify yourself from remote locations
- 💾 Persistent settings - all notification configs saved automatically
🤖 Discord Integration
-
Webhook Mode (One-Way):
- Send mesh messages TO Discord using
#discordcommand - Simple setup with webhook URL
- Perfect for notifications
- Send mesh messages TO Discord using
-
Bot Mode (Two-Way):
- Receive Discord messages and forward TO mesh
- Send mesh messages TO Discord channel
- Full bidirectional communication
- Reacts with ✅/❌ to show send status
-
Both can run simultaneously!
🔧 MQTT Integration
- MQTT bridge - publish mesh messages to MQTT broker
- Bidirectional - receive MQTT messages and forward to mesh
- Topic configuration - customizable topic prefix
- QoS support - quality of service levels
- Retain messages - optional message retention
- 💾 Persistent settings - MQTT config saved automatically
🚨 Emergency Response System
- SOS emergency tracking - auto-detects emergency keywords (#sos, #emergency, #help, #911, mayday, etc.)
- Auto-response - automatically sends help instructions and requests GPS location
- Status tracking - manage emergencies through active, responding, and resolved states
- Critical info display - shows GPS coordinates, battery level, signal strength (SNR)
- Emergency broadcasting - broadcast emergencies to all nodes on the mesh
- Google Maps integration - view emergency locations directly in maps
- NWS weather alerts - fetch and auto-broadcast severe weather warnings from National Weather Service
- Weather monitoring - monitor by state or GPS coordinates with configurable intervals
- Severity-based alerts - color-coded alerts (Extreme, Severe, Moderate, Minor)
- Audio alerts - plays sound when new emergencies or severe weather detected
- Safety protocols - built-in emergency response documentation and procedures
🎨 Modern UI
- Clean, dark-themed interface
- Responsive design with Tailwind CSS
- Real-time updates via WebSocket
- Intuitive navigation
- Mobile-friendly PWA
💾 System Reliability
- Memory management - automatic log rotation (5000 console lines, 1000 messages)
- Age-based cleanup - removes old dedup cache (1 hour retention)
- Memory stats reporting - shows usage every 10 minutes
- Stable reconnection - port-based radio IDs prevent duplicates
- Self-healing - automatic cleanup of disconnected radios
- Perfect for 24/7 - optimized for long-running deployments
- Node.js 18+ (required for bridge server)
- Modern Browser: Chrome, Firefox, Edge, Safari
- Meshtastic Device(s): 1+ radio connected via USB (2+ for message bridging)
- Git: For cloning the repository
- Ollama (optional): For AI assistant features
- SMTP Account (optional): For email notifications
- Discord Webhook/Bot (optional): For Discord integration
# Clone the repository
git clone https://github.com/IceNet-01/Mesh-Bridge-GUI.git
cd Mesh-Bridge-GUI
# Install dependencies
npm install
# Start the application (both bridge server and web UI)
npm run startDevelopment mode starts two servers:
- Bridge Server: http://localhost:8080 (WebSocket)
- Web UI (Vite): http://localhost:5173 (with hot-reload)
Open your browser to http://localhost:5173
Install as a permanent service that starts on boot:
# Build the application first
npm run build
# Install and enable the service (requires sudo)
sudo ./install-service.shThe installation script will:
- ✅ Create systemd service file
- ✅ Enable auto-start on boot
- ✅ Start the service immediately
- ✅ Display service status and logs
Access the Interface:
- Locally: http://localhost:8080
- On LAN: http://YOUR_LOCAL_IP:8080 (shown during startup)
- Example: http://192.168.1.100:8080
The server automatically binds to 0.0.0.0, making it accessible from any device on your local network!
Service Management:
sudo systemctl start mesh-bridge # Start service
sudo systemctl stop mesh-bridge # Stop service
sudo systemctl restart mesh-bridge # Restart service
sudo systemctl status mesh-bridge # Check status
sudo journalctl -u mesh-bridge -f # View live logsUninstall Service:
sudo ./uninstall-service.shBenefits:
- ✅ Starts automatically on system boot
- ✅ Auto-restarts if it crashes (10 second delay)
- ✅ Radio connections persist even when browser is closed
- ✅ Single port (8080) for both WebSocket and web UI
- ✅ Accessible on LAN for remote access
- ✅ Automatic port cleanup on startup
- ✅ Security hardening (NoNewPrivileges, PrivateTmp)
- ✅ All settings persist across restarts
# Build the frontend
npm run build
# Run in production mode (serves both on port 8080)
npm run productionThe bridge server automatically binds to 0.0.0.0, making it accessible from any device on your local network!
Access from other devices:
- Find your server's local IP address (shown on bridge startup)
- Open browser on any device on the same network
- Navigate to
http://YOUR_SERVER_IP:8080 - Full functionality works remotely!
Example:
Bridge Server running on: 192.168.1.100
Access from phone: http://192.168.1.100:8080
Access from tablet: http://192.168.1.100:8080
Access from laptop: http://192.168.1.100:8080
Access locally: http://localhost:8080
Security Considerations:
- The server is accessible to anyone on your LAN
- For internet access, use a reverse proxy (nginx, Caddy) with authentication
- Consider firewall rules if exposing to WAN
- HTTPS recommended for remote access (use reverse proxy)
Finding Your IP Address:
# Linux/macOS
hostname -I | awk '{print $1}'
# Or look for it in the bridge startup logs
npm run bridge
# Shows: 🌐 Access on LAN: http://192.168.1.100:8080 (eth0)For bidirectional Discord ↔ Mesh communication:
-
Create Discord Bot:
- Go to https://discord.com/developers/applications
- Create new application → Add Bot
- Enable MESSAGE CONTENT INTENT (required!)
- Copy bot token
-
Invite Bot to Server:
- OAuth2 → URL Generator
- Select:
botscope - Permissions: Send Messages, Read Message History, Add Reactions
- Use generated URL to invite
-
Get Channel ID:
- Enable Developer Mode in Discord
- Right-click channel → Copy ID
-
Configure in UI:
- Go to Communication Settings
- Enable Discord Bot
- Enter bot token and channel ID
- Save settings
-
Restart bridge - bot will connect automatically!
For simple notifications from mesh TO Discord:
- In Discord: Server Settings → Integrations → Webhooks
- Create webhook, copy URL
- In bridge UI: Communication Settings → Discord Webhook
- Paste webhook URL, save
- Use
#discord [message]from radio!
- Get SMTP credentials (Gmail app password recommended)
- In UI: Communication Settings → Email
- Enter SMTP host, port, username, password
- Test connection
- Use
#email [message]from radio!
- Have MQTT broker URL ready
- In UI: MQTT Settings
- Enter broker URL, credentials, topic prefix
- Test connection
- Messages automatically bridge!
Development Mode (two servers):
┌─────────────────┐
│ Web Browser │ ← You interact here
│ (localhost:5173)│ (Vite dev server with hot-reload)
└────────┬────────┘
│ WebSocket
│
┌────────▼────────┐
│ Bridge Server │ ← Node.js WebSocket server
│ (localhost:8080)│ Persistent configuration
└────────┬────────┘
│ Serial (USB)
┌────┴────┐
│ │
┌───▼──┐ ┌──▼───┐
│Radio1│ │Radio2│ ← Meshtastic devices
└──────┘ └──────┘
Production Mode / Service (single server):
┌─────────────────┐
│ Web Browser │ ← You interact here
│ (localhost:8080)│
└────────┬────────┘
│ HTTP + WebSocket (same port!)
┌────────▼────────┐
│ Bridge Server │ ← Serves static files + WebSocket
│ (localhost:8080)│ All settings persist
└────────┬────────┘
│ Serial (USB)
┌────┴────┐
│ │
┌───▼──┐ ┌──▼───┐
│Radio1│ │Radio2│ ← Meshtastic devices
└──────┘ └──────┘
With 2+ Radios:
- Radio 1 receives mesh message on Channel 0
- Bridge detects message, checks if it's from another bridge radio (loop prevention)
- Finds matching channel on Radio 2 (by PSK + name)
- Forwards message to Radio 2 Channel 0
- Radio 2 broadcasts to its mesh network
- Deduplication prevents the message from being forwarded back
With 1 Radio:
- All commands work (#ai, #weather, #email, #discord, etc.)
- Discord bot can send messages to mesh
- MQTT integration works
- Perfect monitoring and control station!
Send commands from your radio by starting messages with #:
| Command | Description | Example |
|---|---|---|
#ping |
Check if bridge is alive | #ping |
#help |
List available commands | #help |
#status |
Bridge status and info | #status |
#time |
Current date and time | #time |
#uptime |
Bridge uptime | #uptime |
#version |
Software version | #version |
#weather [location] |
Get weather report | #weather Seattle |
#radios |
List connected radios | #radios |
#channels |
List configured channels | #channels |
#stats |
Message statistics | #stats |
#nodes |
List known nodes | #nodes |
#ai [question] |
Ask AI assistant | #ai What is Python? |
#ask [question] |
Ask AI assistant | #ask Explain radio waves |
#email [message] |
Send email | #email Alert: sensor triggered |
#discord [message] |
Send to Discord | #discord Hello from mesh! |
#notify [message] |
Send to email AND Discord | #notify Emergency alert |
Rate Limits:
- Regular commands: 10 per minute per user
- AI commands: 3 per minute per user
Meshtastic radios require GPS for accurate timekeeping. The bridge does NOT sync time over serial - this feature has been intentionally removed.
- ✅ Radios with GPS will maintain accurate time
- ✅ Time is used for message timestamps
- ✅ Internal time tracking from GPS packets (passive)
- ❌ No active time sync over serial
- ❌ No warnings about time accuracy
If your radio time is inaccurate, ensure GPS reception or use the Meshtastic mobile app.
The bridge is optimized for 24/7 operation:
- Console buffer: 5000 lines (auto-rotates)
- Message history: 1000 messages (auto-rotates)
- Dedup cache: 2000 message IDs with 1-hour retention
- Periodic cleanup: Every 10 minutes
- Memory stats: Logged every 10 minutes
This prevents unbounded memory growth on long-running deployments.
The bridge now has robust reconnection:
- Stable radio IDs: Based on port path (not timestamp)
- No duplicates: Same radio always gets same ID
- Auto-cleanup: Disconnected radios removed automatically
- Event-driven: Listens for natural disconnection events
- Self-healing: System recovers gracefully from errors
USB cable unplugs, radio reboots, and port errors are handled gracefully!
All settings are automatically saved to bridge-server/bridge-config.json:
{
"aiEnabled": true,
"email": {
"enabled": true,
"host": "smtp.gmail.com",
"port": 587,
"secure": false,
"user": "your-email@gmail.com",
"password": "your-app-password",
"from": "bridge@example.com",
"to": "recipient@example.com",
"subjectPrefix": "[Meshtastic]"
},
"discord": {
"enabled": true,
"webhook": "https://discord.com/api/webhooks/...",
"username": "Meshtastic Bridge",
"avatarUrl": "",
"botEnabled": true,
"botToken": "your-bot-token",
"channelId": "your-channel-id"
},
"mqtt": {
"enabled": true,
"brokerUrl": "mqtt://broker.hivemq.com:1883",
"username": "",
"password": "",
"topicPrefix": "meshtastic",
"qos": 0,
"retain": false
}
}This file is created automatically when you save settings in the UI. No manual editing required!
- Check USB connection:
ls /dev/tty* - Permissions: Add user to
dialoutgroup (Linux)sudo usermod -a -G dialout $USER - Restart after group change:
sudo reboot - Check logs: Look for connection errors in console
If you see "Cannot lock port" errors:
- Make sure no other software is using the port
- Disconnect radio, wait 5 seconds, reconnect
- Restart bridge server
- The bridge now handles port locks gracefully!
- Check token: Make sure it's correct
- Enable intents: MESSAGE CONTENT INTENT must be enabled
- Invite bot: Make sure bot is in your server
- Check channel ID: Right-click channel → Copy ID
- Restart bridge: Bot connects on startup
This happens if you saved settings with the masked webhook URL. Fix:
- Edit
bridge-server/bridge-config.json - Replace
"webhook": "(configured)"with your real webhook URL - Restart bridge
Mesh-Bridge-GUI/
├── bridge-server/ # Backend Node.js server
│ ├── index.mjs # Main server file
│ ├── protocols/ # Radio protocol handlers
│ └── bridge-config.json # Auto-generated config
├── src/ # Frontend React app
│ └── renderer/
│ ├── components/ # UI components
│ ├── store/ # Zustand state management
│ └── types.ts # TypeScript types
├── dist/ # Production build output
└── package.json # Dependencies
npm run build # Build frontend
npm run production # Run in production mode
npm run start # Dev mode (both servers)
npm run bridge # Bridge server only
npm run dev # Frontend onlyContributions welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes
- Test thoroughly
- Submit pull request
Dual License:
- Non-Commercial Use: Free for personal, educational, and non-commercial use
- Commercial Use: Requires commercial license
See LICENSE file for details.
Built with:
- @meshtastic/core - Official Meshtastic library
- @meshtastic/transport-node-serial - Serial transport
- React - UI framework
- TypeScript - Type safety
- Tailwind CSS - Styling
- Zustand - State management
- Ollama - Local AI models
- discord.js - Discord bot integration
- 📧 Issues: https://github.com/IceNet-01/Mesh-Bridge-GUI/issues
- 💬 Discussions: https://github.com/IceNet-01/Mesh-Bridge-GUI/discussions
Happy Meshing! 📡