Automated sync from private repository. Built with obfuscation enabled.
openHop Console
A real-time web dashboard for MeshCore LoRa mesh repeaters.
openHop Console gives you full visibility into your MeshCore network — packet flow, topology, signal quality, RF metrics, GPS diagnostics, and radio configuration — through a single browser tab. It layers on top of openHop Repeater without replacing the Repeater service.
Quick Start
Requirements
- Raspberry Pi (3, 4, 5, or Zero 2 W)
- LoRa radio module (SX1262 or SX1276)
- Raspberry Pi OS (Bookworm recommended)
Prerequisite: openHop Repeater
The Console dashboard plugs into an existing openHop Repeater install. If you don't already have it running, install Repeater first using the Repeater repo's manage.sh:
git clone https://github.com/openhop-dev/openhop_repeater.git
cd openhop_repeater
sudo bash ./manage.sh install
Repeater handles system dependencies, the /opt/openhop_repeater virtualenv, radio/GPIO configuration, /etc/openhop_repeater/config.yaml, and the openhop-repeater systemd service.
Install the Console
git clone https://github.com/Treehouse-00/pymc_console.git pymc_console
cd pymc_console
sudo bash manage.sh install
This downloads the latest Console release, extracts it to /opt/pymc_console/web/html/, and points Repeater's web.web_path at it. Open http://<your-repeater-ip>:8000 in a browser.
Upgrade the Console
cd pymc_console
sudo bash manage.sh upgrade
Refreshes the dashboard assets in place. Your web_path setting is preserved. Repeater, core, and config are untouched. For upgrading openHop Repeater itself, use the Repeater repo's manage.sh.
Uninstall the Console
cd pymc_console
sudo bash manage.sh uninstall
Removes /opt/pymc_console and this repo. openHop Repeater is not touched — use the Repeater repo's manage.sh to remove it.
Non-interactive Mode
All prompts can be auto-confirmed for automation:
sudo bash manage.sh --yes install
ASSUME_YES=1 sudo -E bash manage.sh upgrade
How It Fits Together
┌─────────────────────────────────────────────────────────────┐
│ openHop Console │
│ (this repo — web dashboard UI) │
│ │
│ • React SPA served on port 8000 │
│ • Real-time packets, topology, stats, radio config │
│ • manage.sh installs/upgrades the Console dashboard only │
└─────────────────────┬───────────────────────────────────────┘
│ uses API from
┌─────────────────────▼───────────────────────────────────────┐
│ openHop Repeater │
│ (openHop repeater daemon) │
│ │
│ • Python daemon running the LoRa repeater │
│ • REST API + WebSocket on port 8000 │
│ • Packet forwarding, logging, radio control │
└─────────────────────┬───────────────────────────────────────┘
│ built on
┌─────────────────────▼───────────────────────────────────────┐
│ openHop Core │
│ (MeshCore protocol library) │
│ │
│ • Low-level MeshCore protocol implementation │
│ • Radio drivers (SX1262, SX1276) │
│ • Packet encoding/decoding │
└─────────────────────────────────────────────────────────────┘
Key points:
- Console does not replace Repeater — they work together
- Repeater is installed separately using openHop Repeater's
manage.sh; Console'smanage.shonly layers the dashboard on top - You can upgrade Console independently without touching Repeater
Features
Topology Analysis
Reconstructs your network's structure from packet paths using a Viterbi HMM decoder — resolving prefix collisions with physics-based RF constraints and real-world observation evidence.
- One-click Deep Analysis from up to 14 days of packet history
- Viterbi HMM decoding — resolves 2-char prefix collisions using geographic distance and LoRa range constraints
- Ghost node discovery — detects unknown repeaters when no known candidate fits
- 7-phase topology pipeline — directional edges, betweenness centrality, mobile detection, TX delay recommendations, path health scoring
- 3D terrain — AWS Terrarium elevation with hillshading; markers and edges drape onto the landscape
- Wardriving overlay — H3 hexagonal coverage tiles with SNR-based coloring
- Edge confidence — line thickness scales with observation count; color indicates certainty
Link Quality Radar
Polar chart placing all contacts at their actual compass bearing and distance from your node.
- Zero-hop neighbors colored by SNR (green → yellow → orange → red)
- Multi-hop contacts at 33% opacity to distinguish direct RF from relayed
- Hover tooltips with full signal metrics (RSSI, SNR, distance, last seen)
Statistics Dashboard
Comprehensive RF metrics and network composition analysis.
- Airtime utilization — RX/TX stacked area charts with peak and mean metrics
- Packet type distribution — treemap of ADVERT, TXT_MSG, ACK, TRACE, etc.
- Network composition — repeater / companion / room server breakdown
- Noise floor heatmap — interference patterns over time
- Disambiguation health — Viterbi confidence metrics and ghost node stats
- TX delay recommendations — slot-based timing optimization per node role
Packet Path Tracing
Click any packet to visualize its route through the mesh with hop-by-hop confidence.
- Confidence coloring — Green (100%), Yellow (50–99%), Orange (25–49%), Red (<25%), Gray (ghost)
- Interactive map showing resolved path with intermediate hops
- Signal details — RSSI, SNR, and timing per packet
- Byte-level breakdown — header fields, payload structure, raw hex
Themes & Terminal
Two polished color schemes and a built-in CLI for direct repeater interaction.
- Breeze Dark / Breeze Light — KDE Breeze-inspired themes with full design token system
- Terminal — interactive CLI mapped to API endpoints (get/set radio, ping, diagnostics)
- Packet capture —
start cap/end cap/export capfor timed diagnostic snapshots - Live logs — streaming from repeater with DEBUG/INFO toggle
All Pages at a Glance
| Page | Route | What it does |
|---|---|---|
| Dashboard | / |
Live packet counters, sparkline trends, LBT widgets, recent packets |
| Packets | /packets |
Searchable packet history with detail modal, path visualization, byte breakdown |
| Contacts | /contacts |
MapLibre GL map with topology edges, ghost nodes, terrain, wardriving overlay |
| Statistics | /statistics |
µPlot charts — airtime, packet types, noise floor, signal scatter, network composition |
| Mesh Graph | /meshgraph |
GPU-accelerated force-directed graph (Cosmograph) |
| System | /system |
CPU, memory, disk, temperature, processes, network I/O |
| Logs | /logs |
Live log stream with level filtering |
| Terminal | /terminal |
Interactive CLI — MeshCore commands, ping, diagnostics, captures |
| Configuration | /configuration |
Radio settings, TX delays, transport keys, identity, theme, stealth location |
Management
manage.sh Commands
manage.sh is Console-only. Service control, status, logs, updates, and radio/GPIO configuration are all handled by openHop Repeater.
sudo bash manage.sh --help
| Verb | Action |
|---|---|
install |
Install the Console dashboard into /opt/pymc_console and point web_path at it. Requires openHop Repeater to be installed. |
upgrade |
Refresh the dashboard assets in place. Preserves your web_path and self-updates this repo from origin/main. Repeater stays untouched. |
uninstall |
Remove /opt/pymc_console and this repo. Does NOT touch openHop Repeater. |
Flags: --yes / -y (or ASSUME_YES=1) auto-confirms prompts; NO_COLOR=1 disables ANSI output.
Radio & GPIO Configuration
Radio and GPIO settings are managed by openHop Repeater:
cd ~/openhop_repeater && sudo bash ./manage.sh
Or edit the config directly:
# /etc/openhop_repeater/config.yaml
radio:
frequency: 927875000 # Hz
spreading_factor: 7 # SF7–SF12
bandwidth: 62500 # Hz
tx_power: 28 # dBm
coding_rate: 6 # 4/5, 4/6, 4/7, 4/8
You can also change radio settings live from the Configuration page in the dashboard — no SSH required.
DIO2 / DIO3 Pin Configuration
Some LoRa modules need specific DIO pin settings. These are independent — enabling one does not affect the other:
- DIO3 (TCXO) — set
use_dio3_tcxo: trueif your module has a temperature-compensated oscillator on DIO3 - DIO2 (RF Switch) — set
use_dio2_rf: trueif your module uses DIO2 for TX/RX antenna switching
radio:
use_dio3_tcxo: true
use_dio2_rf: true # dev branch only
Service Management
Service lifecycle belongs to openHop Repeater's systemd unit. Use systemctl / journalctl directly:
sudo systemctl status openhop-repeater # Check status
sudo systemctl restart openhop-repeater # Restart
sudo journalctl -u openhop-repeater -f # Live logs
manage.sh does not wrap these commands — they are Repeater's responsibility.
Directory Layout
After installation:
~/pymc_console/ ← This repo (cloned by you)
~/openhop_repeater/ ← openHop Repeater source (cloned by you)
/opt/openhop_repeater/ ← Installed Repeater (owned by Repeater manage.sh)
/opt/pymc_console/web/html/ ← Installed dashboard (owned by our manage.sh)
/etc/openhop_repeater/config.yaml ← Radio + Repeater config (we patch web.web_path only)
/var/log/openhop_repeater/ ← Repeater log files
Hardware
Supported Boards
- Raspberry Pi 3, 4, 5
- Raspberry Pi Zero 2 W
- Any SBC with SPI and GPIO (untested but likely works)
Tested Radio Modules
- Waveshare SX1262 HAT
- Ebyte E22 modules
- LILYGO T3S3 (via USB serial)
- Heltec LoRa 32
Connection
LoRa module connects via SPI with GPIO pins for reset, busy, and DIO1. Pin mapping is configured during installation via openHop Repeater's manage.sh.
Troubleshooting
Dashboard won't load
- Is the service running?
sudo systemctl status openhop-repeater - Is port 8000 responding?
curl -s http://localhost:8000/api/stats | head -c 100 - Check for errors:
sudo journalctl -u openhop-repeater -n 50
Login fails / "Error 200"
Usually caused by a version mismatch between Console and Repeater. Update both:
# Update openHop Repeater
cd ~/openhop_repeater && sudo bash ./manage.sh upgrade
# Update the Console dashboard
cd ~/pymc_console && sudo bash manage.sh upgrade
No packets being received
-
SPI enabled?
ls /dev/spidev*If no devices listed, enable SPI via
raspi-config→ Interface Options → SPI. -
GPIO correct? Run openHop Repeater's manage.sh → Configure GPIO and verify pin assignments match your wiring.
-
Frequency match? Confirm your radio frequency matches the rest of your mesh network.
-
Check the logs:
sudo journalctl -u openhop-repeater -n 100 | grep -i "error\|fail\|radio"
Service won't start
# Check for config syntax errors
python3 -c "import yaml; yaml.safe_load(open('/etc/openhop_repeater/config.yaml'))"
# Check for Python dependency issues
/opt/openhop_repeater/venv/bin/python -m pip show openhop_repeater openhop_core
"Radio presets file not found" during install
Non-fatal warning. The installer fetches presets from an API; if unavailable, it falls back to common defaults. Installation continues normally.
Dashboard loads but shows no data
- The dashboard requires authentication. If you see the login page, use the credentials you set during openHop Repeater installation.
- If you're on a fresh install, allow 30–60 seconds for the repeater to initialize and begin receiving packets.
- Check that WebSocket is connecting: open browser DevTools → Network → WS. You should see an active
/ws/packetsconnection.
Upgrade didn't take effect
Hard-refresh your browser (Cmd+Shift+R / Ctrl+Shift+R) to clear the cached SPA bundle. Vite hashes filenames, but the browser may still cache index.html.
Under the Hood
Viterbi Path Disambiguation
MeshCore packets contain 2-character hex prefixes representing the route through the mesh:
Path: ["FA", "79", "24", "19"]
Origin → Hop1 → Hop2 → Local
Multiple nodes can share the same 2-char prefix (1-in-256 collision). The system uses a Viterbi HMM decoder to find the most probable sequence of actual nodes:
- States — all candidate nodes matching each prefix, plus a "ghost" state for unknowns
- Priors — recency-weighted (recently-seen nodes are more likely)
- Transitions — physics-based costs using geographic distance and LoRa range constraints
- Key principle — when edge observations have ≥80% confidence, real-world evidence overrides physics
Before Viterbi decoding, candidates are scored using four-factor analysis:
- Position (15%) — typical path positions for this prefix
- Co-occurrence (15%) — which prefixes appear adjacent
- Geographic (35%) — distance to dual-hop anchor points
- Recency (35%) — exponential decay
e^(-hours/12)
Ghost Node Discovery
When no known candidate is geographically plausible, the decoder selects a "ghost" state. These are clustered and classified into four tiers:
- Confirmed — very high observation count, consistent neighbors, plausible location
- Likely — strong evidence, probably a real undiscovered repeater
- Possible — moderate evidence, worth investigating
- Noise — low evidence, likely path artifacts
Ghost clusters include RF-constrained location estimates, temporal consistency analysis, and collision detection against known nodes.
7-Phase Topology Pipeline
- Directional edge tracking — forward/reverse counts, symmetry ratio
- Path sequence registry — all observed paths, canonical detection
- Flood vs direct classification — per-edge routing type
- Edge betweenness centrality — backbone identification
- Mobile repeater detection — path volatility analysis
- TX delay recommendations — slot-based timing optimization
- Path health scoring — combined health, weakest link, latency
Protocol Library
The frontend includes a complete TypeScript port of the MeshCore protocol — binary packet parsing, header bit-field extraction, per-type payload decoders, channel key derivation, and GRP_TXT decryption (SHA-256, AES-ECB, pure JS — no native dependencies).
Standalone UI Installation
If you already have openHop Repeater running and just want the dashboard, see INSTALL.md for manual tar.gz installation without manage.sh.
License
MIT — See LICENSE
Credits
Built on the work of:
- RightUp — Creator of the original pyMC Repeater/Core projects and a maintainer of the MeshCore Python ecosystem
- openHop Repeater — Core repeater daemon
- openHop Core — Protocol library
- MeshCore — The MeshCore project and community
- d40cht/meshcore-connectivity-analysis — Viterbi HMM approach for path disambiguation
- meshcore-bot — Recency scoring and dual-hop anchor disambiguation




