Connect via MeshCore¶
Status: shipped (Companion-over-USB). MeshCore is a first-class DAPPS backhaul bearer:
a node can carry BackhaulMessages over a MeshCore radio's private channel alongside (or
instead of) AGW/AX.25. It's off by default — set MeshCoreEnabled=true to turn it on.
The KISS-driven flavour (below) is still future work.
Why MeshCore¶
MeshCore is a small, modern mesh radio firmware (LoRa-shaped today) with a built-in routing layer that's a much better fit for slow, lossy, mostly-unreliable RF than AX.25. As a bearer for DAPPS it gives:
- A datagram interface (no AX.25 connection setup, no T1/T2/T3 timer dance) — better for short, frequent messages.
- A working hop-by-hop mesh underneath, so DAPPS doesn't have to solve "how do I reach a node three hops away" itself.
- A real low-cost long-haul story for operators without HF.
What's implemented (H1 — Companion over USB)¶
DAPPS talks to a Heltec-class radio running MeshCore Companion (USB) firmware (tested on
v1.16.0) over the serial Companion protocol, using the binary channel-data path. The bearer
lives in dapps.meshcore and is wired in behind the standard bearer seam; see
src/dapps/dapps.meshcore/README.md
for the deep detail. Feature set:
- Binary transport + heavy compression — messages are encoded with the real DAPPS codec, zstd-compressed against a versioned shared dictionary (~3× goodput, usually one LoRa packet/message), fragmented to the ~165 B payload, and sent as channel-data floods.
- Good-citizen controls — a hard trailing-hour airtime governor plus adaptive listen-before-talk and congestion backoff (from overheard-packet occupancy), so DAPPS rides a shared preset politely.
- End-to-end reliability — the channel is a fire-and-forget flood, so DAPPS adds its own datagram-style ACK + resend with idempotent (dedup-by-id) delivery. Not a session protocol.
- Passive discovery — a node auto-learns peers it hears over MeshCore and routes to them with no manual neighbour config.
- Watchdog + recovery — detects a hung radio and hard-resets it over CP2102 DTR/RTS, then re-applies config.
- Device control — region presets, TX power (region-capped), channel name + PSK, node name, all from the dashboard or config; live status + a Reset-radio button.
Enabling it¶
Configure via the dashboard Settings page, the systemoptions table, or DAPPS_MESH_CORE_*
environment variables (note the underscore in MESH_CORE). The essentials:
DAPPS_MESH_CORE_ENABLED=true
DAPPS_MESH_CORE_PORT=/dev/ttyUSB0
DAPPS_MESH_CORE_REGION=uk-test # preset: uk-narrow | uk-test | eu-legacy | custom
DAPPS_MESH_CORE_CHANNEL_NAME=dapps
DAPPS_MESH_CORE_CHANNEL_PSK=<32-char hex, or a passphrase>
DAPPS_MESH_CORE_TX_POWER_DBM=8 # capped by the region's regulatory max
Other options (all optional, sensible defaults): NODE_NAME, CHANNEL_INDEX,
AIRTIME_BUDGET_SECONDS_PER_HOUR, COMPRESS, CONGESTION_BACKOFF_FRACTION, LBT_GUARD_MS,
RELIABLE_DELIVERY, plus the deployment-model knobs below. The full table is in the
library README.
Runtime status/control: GET /MeshCore/status, POST /MeshCore/reset.
Deployment models — privacy vs containment¶
A MeshCore private channel gives privacy (PSK) but not containment: channel messages flood unscoped by default and any same-preset Repeater relays them network-wide without the PSK. Pick a model per node (details + firmware caveats in the library README's "Containment"):
| Model | Config | What you get |
|---|---|---|
| A unscoped public preset | REGION=uk-narrow, no scope |
free public-repeater carriage everywhere — relies on the good-citizen controls; light traffic only |
| B scoped public preset | REGION=uk-narrow + FLOOD_SCOPE_KEY=<name> |
your floods are dropped by repeaters that don't share the scope; needs your own scoped repeaters to carry between DAPPS nodes |
| C dedicated preset | REGION=custom + CUSTOM_PRESET=freq=868.4;bw=62.5;sf=8;cr=8;pwr=14 |
total physical isolation on your own frequency/SF — least config risk |
What stays the same¶
- The DAPPSv1 application layer is unchanged — an app doesn't know or care whether the bearer is AGW, UDP, or MeshCore.
- Discovered/neighbour peers hold MeshCore reachability alongside other bearers (a
MeshCoreChannelhint on the route); the routing resolver picks per destination by cost. - Dashboard, REST, MQTT, MCP — unchanged.
Routing implications¶
MeshCore does its own mesh routing under DAPPS, so the two hop-count models stack: a message that's "one hop" to DAPPS may traverse several MeshCore hops underneath. DAPPS routes by cost hints rather than raw hop counts (MeshCore is a mid-cost RF class), so this works out; it's worth understanding when tuning a multi-bearer setup. Background: docs/meshcore-backhaul-routing.md.
How it's been validated¶
- On air, two Heltec V3s: bidirectional exchange, watchdog reset+recovery, the airtime governor and adaptive backoff, reliability recovering ~40 % induced loss with no duplicate delivery, passive discovery, and both the custom preset and the flood-scope key being accepted by real firmware.
- In simulation (
dapps.meshcore.sim), the real bearer runs over an in-process multi-hop mesh the two bench radios can't reach: multi-hop flood + dedup-across-paths, flood-scope containment, and reliability recovering every message over four hops at 30–40 % per-edge loss (delivered exactly once). - Not yet validated on hardware: a repeater actually dropping an out-of-scope flood — that needs a physical Repeater node + attenuators, which is the next hardware step.
Future¶
- MeshCore KISS (H2) — for operators with a radio already in KISS-TNC mode, DAPPS driving MeshCore frames itself. Planned, not yet implemented.
- Firmware flash from DAPPS — managing/upgrading radio firmware from the node.
If you've got a MeshCore radio and want to test, open an issue.