vccs
Vue 3용 차트 컴포넌트 모음, Recharts에서 포팅
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Real-time Meshtastic mesh network visualization
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
🌐 Website: meshpulse.app · 📡 Coverage Server: coverage.meshpulse.app
Real-time web-based visualization of Meshtastic mesh network nodes. Optimized for low-power devices like Raspberry Pi Model B+.README 전문 보기 ↗🌐 Website: meshpulse.app · 📡 Coverage Server: coverage.meshpulse.app
Real-time web-based visualization of Meshtastic mesh network nodes. Optimized for low-power devices like Raspberry Pi Model B+.
The map shows true network status based on when your tracker last heard each node:
| Color | Age | Meaning |
|---|---|---|
| 🟢 Green | < 1 hour | Recently heard - node is active |
| 🟡 Yellow | 1-6 hours | Not heard recently - may be inactive or out of range |
| 🔴 Red | > 6 hours | Stale - tracker hasn't heard this node for a long time |
Note: This reflects your tracker's perspective. A "red" node may still be active in the mesh but simply not heard by your specific tracker due to distance or obstacles.
sudo apt update
sudo apt install python3 python3-pip lighttpd git -y
pip3 install meshtastic websockets --break-system-packages
# Add user to dialout group (for USB access)
sudo usermod -aG dialout $USER
# Logout and login for group change to take effect
cd ~
git clone https://github.com/maxg10/meshpulse.git
cd meshpulse
./install.sh
sudo systemctl start meshpulse
sudo systemctl status meshpulse
http://YOUR_PI_IP/meshpulse/
The easiest way to run MeshPulse on any platform — Linux, Mac, Windows, Raspberry Pi. No dependencies to install except Docker.
⚠️ Docker version supports TCP/WiFi connections only. USB serial is not supported in containers.
curl -fsSL https://get.docker.com | shgit clone https://github.com/maxg10/meshpulse.git
cd meshpulse
cp .env.example .env
# Edit .env — set your tracker IP:
# TRACKER_HOST=192.168.1.103
docker compose up -d
TRACKER_HOST accepts host or host:port (default port 4403, e.g. host.docker.internal:4405 for meshtasticd virtual nodes).
Open: http://localhost/meshpulse/
git pull
docker compose pull
docker compose up -d
"8080:80" in docker-compose.ymlIf you prefer to install manually or want to understand what's happening:
# 1. Install dependencies
sudo apt update
sudo apt install python3 python3-pip lighttpd -y
pip3 install meshtastic websockets --break-system-packages
# 2. Start and enable web server
sudo systemctl enable lighttpd
sudo systemctl start lighttpd
# 3. Clone repository
cd ~
git clone https://github.com/maxg10/meshpulse.git
cd meshpulse
# 4. Setup web directory
sudo mkdir -p /var/www/html/meshpulse
sudo cp frontend/index.html /var/www/html/meshpulse/
sudo cp frontend/favicon.ico /var/www/html/meshpulse/
sudo chown -R $USER:$USER /var/www/html/meshpulse
# 5. Test manually (important!)
python3 backend/meshpulse.py
# Press Ctrl+C after 2-3 minutes once you see nodes appearing
# 6. Verify JSON was created
cat /var/www/html/meshpulse/nodes.json
# 7. Open in browser to test
# http://YOUR_PI_IP/meshpulse/
# 8. Install systemd service using install.sh
./install.sh
# 9. Start service
sudo systemctl start meshpulse
sudo systemctl status meshpulse
cd ~/meshpulse
git pull
./migrate.sh # copies data files, sets up /meshtastic/ → /meshpulse/ redirect
./install.sh
sudo systemctl restart meshpulse
migrate.shonly needs to run once. It copies your existing data and sets up the backward-compat redirect. After this first upgrade, skip it.
cd ~/meshpulse
./update.sh
That's it — pulls latest code, installs frontend files, restarts service.
🗺️ Overlays follow the map's filters — Plugins can read the node set the map is actually showing (api.nodes.getVisible()) and re-render when a filter changes (api.nodes.onFilterChange()). Uncheck Meshtastic and a plugin's labels now disappear along with the markers instead of floating over an empty map. Weather Overlay 1.0.3 uses it.
💾 Plugin tabs from the manifest — A plugin that declares frontend.pages[] gets a navbar tab on every page, with no plugin JavaScript required — so a backend-only plugin can ship a browsable page. This is what finally opens the BBS web viewer: read the boards, netmail and node directory in a browser instead of typing !bbs read over radio.
💬 Plugins can answer the browser — New on_ws_request(data, channel, reply) hook lets a plugin respond to the client that asked, instead of broadcasting every reply to every open browser.
♻️ Plugin updates actually take effect — Plugin assets are loaded with a version query, so upgrading a plugin from the store no longer leaves the browser running the old code from cache.
🔗 One path, no redirect — The frontend asks for /meshpulse/ directly. Previously every navbar click and every plugin asset relied on a /meshtastic/ → /meshpulse/ redirect rule; without it, plugins 404'd.
🌐 Multi-network map — Nodes now carry a network tag (net): Meshtastic and Meshcore nodes live side by side on the same map. Non-Meshtastic nodes render as diamond markers, and per-network checkboxes in Mesh Info let you toggle whole networks on/off (persisted across refreshes).
🔌 Plugin node-injection API — Plugins can inject nodes from non-Meshtastic sources via inject_node() (new node_inject permission). Injected nodes are first-class citizens: TTL cleanup, persistence, WebSocket updates and map rendering all apply. This powers the new Meshcore plugin.
📡 TCP host:port — The TCP connection target now accepts host:port (default 4403), so meshtasticd virtual nodes on custom ports work out of the box.
🔒 Supply-chain security — SBOM generation on every release (CycloneDX + SPDX via Syft), OpenSSF Scorecard workflow, SECURITY.md and Dependabot updates.
🌡️ Weather Overlay plugin — Visualize environment sensor data (temperature, humidity, pressure) as map labels and a heatmap with an absolute temperature scale. Available in the Plugin Store.
🔌 Smoother plugin installs — Installing a plugin from the UI now copies its frontend assets to the web root immediately (with correct ownership), so it works without re-running install.sh.
📟 NodeDB backfill on connect — Hardware model, position and device metrics are read from the tracker's local NodeDB right after connecting, so node popups show hardware info immediately instead of waiting hours for over-the-air NodeInfo.
📱 Mobile fix — The traceroute panel no longer overflows off-screen on narrow phones; it's now centred and fully visible.
📥 Stats CSV exports — Export any table or chart on the Stats page (Most Active Nodes, Recent Anomalies, all 7 charts, combined Radio Stats History) plus an "Export All Nodes" button, all emoji-safe.
🗺️ Sharper map lines — Direct/neighbor links now have a dark casing/halo and traceroutes render on their own top pane, so paths stay readable on any basemap.
📟 Richer node info — Popups show hardware model, licensed (HAM) flag, channel utilization, air-util-TX, GPS sats-in-view and environment metrics; new "Show only licensed" filter. Telemetry fields no longer flicker on NodeInfo rebuilds.
🏪 Plugin Store — Browse and install plugins directly from your mapper. Visit meshpulse.app/plugins or open Config → Plugins.
📡 MQTT Proxy Plugin — Trackers without WiFi can now publish to MQTT brokers through the mapper. Zero configuration — reads settings from tracker firmware. Install from Plugin Store
⛰️ Elevation Map Plugin — Topographic tile overlay with 3 providers. Install from Plugin Store
💾 Backup & Restore — Quick backup (config + plugin settings) or full backup (+ nodes & stats). Download as ZIP, restore with one click.
🔌 Plugin System — Build your own plugins! Backend Python + Frontend JavaScript, 12 hooks, full API. See Plugin Developer Guide.
📊 Anonymous Telemetry
📥 CSV Export
📊 Stats Improvements
Config page (new):
Messages page (new):
Bug fixes:
v1.19 - Serial Python API & Traceroute Improvements
v1.18 - Navigation & Stats Improvements
v1.16 - Python Meshtastic API for TCP
meshtastic.tcp_interface.TCPInterface used for TCP connections (no CLI subprocess)v1.13 - Traceroute & LOS Improvements
v1.11 - RF Line of Sight Analysis
v1.10 - Enhanced Popups & Tracker Marker
v1.9 - Safari WebSocket Fix
v1.8 - Direct Connection Lines & Heat Map
v1.7 - Message Persistence & Cache Busting
nodes.json and restored on restart/page refreshv1.6 - WebSocket & Text Messages
v1.5 - Universal Installer
install.sh for easy installationv1.4 - Accurate Node Status & Tracker Info
lastHeard timestamp from tracker's memoryv1.3 - Max Range Feature
v1.2 - TTL & Multi-Tracker Support
# Backup current nodes.json
cp /var/www/html/meshpulse/nodes.json ~/nodes_backup_$(date +%Y%m%d).json
If your tracker has WiFi (Heltec V3, T-Beam, T-Deck, Station G2), you can connect over TCP instead of USB:
USB to TCPThe backend will restart automatically with the new connection settings. Your choice is saved to config.json and persists across service restarts.
Benefits:
Requirements:
Edit backend/meshpulse.py if needed:
self.port = '/dev/ttyUSB0' # Change if tracker on different port (auto-detected by default)
self.json_path = '/var/www/html/meshpulse/nodes.json' # Output path
self.max_age = 172800 # Node TTL in seconds (48 hours default)
Nodes older than max_age seconds are automatically removed:
# In backend/meshpulse.py, find the ListenBasedMapper instantiation:
mapper = ListenBasedMapper(port, max_age=172800) # 48 hours (default)
# Change to:
mapper = ListenBasedMapper(port, max_age=86400) # 24 hours
# Or:
mapper = ListenBasedMapper(port, max_age=172800) # 48 hours
For the max range feature to work, your local tracker must have a position set:
meshtastic --port /dev/ttyUSB0 --setlat 52.XXXXX --setlon 16.XXXXX
backend/meshpulse.py)meshtastic --listen mode to capture node informationfrontend/index.html)nodes.json every 15 seconds{
"ts": 1736625600,
"updated": "2025-01-11T18:40:00",
"cnt": 7,
"cnt_no_pos": 2,
"max_distance_km": 7.0,
"farthest_node": "!e36738ab",
"tracker": {"id": "!7b6c8272", "model": "TBEAM", "firmware": "2.5.1"},
"nodes": [
{
"id": "!7b6c8272",
"name": "Node Name",
"lat": 52.353434,
"lon": 16.865690,
"alt": 75,
"snr": 10.8,
"role": "ROUTER",
"hops": 0,
"ts": 1736625600
}
],
"nodes_no_pos": [],
"messages": [
{
"from_id": "!7b6c8272",
"from_name": "Node Name",
"text": "Hello mesh!",
"timestamp": 1736625600,
"is_dm": false
}
]
}
# Check lighttpd status
sudo systemctl status lighttpd
# Start if stopped
sudo systemctl start lighttpd
# Check if port 80 is listening
sudo netstat -tlnp | grep :80
# Check logs
sudo journalctl -u meshpulse -n 50
# Test script manually
python3 ~/meshpulse/backend/meshpulse.py
# Verify paths in service
sudo systemctl cat meshpulse
# Check USB devices
ls -la /dev/ttyUSB* /dev/ttyACM*
# Check if user has access (add to dialout group)
sudo usermod -aG dialout $USER
# Log out and back in for group changes to take effect
# Test meshtastic connection
meshtastic --port /dev/ttyUSB0 --info
# Check JSON file
cat /var/www/html/meshpulse/nodes.json
# Verify web server
curl http://localhost/meshpulse/nodes.json
If you're using Heltec V3 and getting "Timed out waiting for connection completion" errors, add --no-nodes flag to the command in backend/meshpulse.py:
cmd = [self.meshtastic_cmd, '--port', self.port, '--listen', '--no-nodes']
Raspberry Pi Model B+ (512MB RAM):
Faster devices: Will have near-instant node discovery.
meshtastic --nodes times out on slow Pi - we use --listen mode insteadExtend the mapper with plugins from the Plugin Store.
All plugins live in one monorepo — maxg10/meshplugins — each released under its own tag with a .meshplugin package attached.
Available plugins:
Install plugins from Config → Plugins → Plugin Store, or download from meshpulse.app/plugins.
See Plugin Developer Guide for a step-by-step tutorial, or Plugin Architecture Docs for the full API reference.
Pull requests welcome! Areas for improvement:
This project is licensed under the GNU General Public License v3.0 — see the LICENSE file for details.
Every GitHub release includes SBOM files in CycloneDX and SPDX JSON formats, generated automatically with Syft. You can scan them for known vulnerabilities with tools like grype:
grype sbom:meshpulse-vX.Y.Z-sbom.cdx.json
Mariusz "Max" Gieparda
📧 [email protected] 🐙 github.com/maxg10 📦 github.com/maxg10/meshpulse
Built with ❤️ for the Meshtastic community in Poland 🇵🇱
Feel free to reach out with questions, bug reports, or feature requests!
Built for the Polish Meshtastic community around Poznań 🇵🇱
Active nodes in Poznań metro area
Questions? Open an issue on GitHub!
© 2025-2026 Mariusz Gieparda (MG Group — mg-group.ltd) | GPL-3.0 License