archy-messh/docs/ARCHITECTURE.md

9.4 KiB

archy-messh — Architecture & Design Space

Target hardware (locked in): Heltec WiFi LoRa 32 V3 — ESP32-S3FN8, 8MB flash, 512KB SRAM, no PSRAM, single SX1262 LoRa radio, dual-core LX7.

This doc synthesizes research into Meshtastic, MeshCore, and Reticulum/RNode firmware architecture, identifies the hard constraints the Heltec V3 imposes, and lays out a phased plan toward a single device that can speak to and repeat between all three networks.


1. The three protocols, side by side

Meshtastic MeshCore Reticulum (RNS + RNode)
Default LoRa PHY (EU) 869.525 MHz, BW 250 kHz (or 812.5 wide), CR 4:5, SF 11 Compiled default identical: 869.525,250,11,5 — but community has since moved to narrowband SF7-8 / BW 62.5 kHz (Oct 2025) No fixed preset — operator-configured freq/BW/SF/CR per deployment
Sync word Custom 0x2b (not LoRaWAN's public 0x34) Unconfirmed — needs direct check (RadioLib default vs custom) Whatever the operator configures; not protocol-mandated
Frequency selection Hashed per channel-name within region plan (hops, not fixed) Single fixed frequency, no hopping found Fixed, operator-set
Radio chips SX126x/SX127x/SX128x/LR11x0/STM32WLE5JC, via RadioLib Same chip families, also via a RadioLib-based wrapper SX127x/SX126x/SX128x, custom firmware (not RadioLib)
On-air routing Pure managed flood: hop_limit/hop_start (3-bit), SNR-weighted CSMA-style contention-window rebroadcast delay, optional learned next-hop since v2.5 Flood-to-discover path, then source-routed reply using a recorded path (path bytes embedded in packet); dedup by content hash (path-independent), 160-entry ring buffer Host-side Transport: announce-based path tables, store-and-forward, no on-device routing at all — RNode is a dumb modem
Encryption AES-CTR (no auth tag), PSK = literal channel key bytes; default "LongFast" key is hardcoded/public in source Public-channel PSK also a known default; DMs use per-contact ECDH (Ed25519→X25519) secret; routing metadata (hashes/path) is cleartext, payload only is encrypted — repeaters relay without decrypting Ed25519 (signing) + X25519 (ECDH, per-packet and per-link) + AES-256-CBC + HMAC-SHA256; destination = truncated SHA-256 hash, not directly tied to a readable identity
Node identity NodeNum from device MAC Self-generated Ed25519 keypair, persisted to flash Identity = Ed25519+X25519 keypair; Destination = hash derived from identity + app name
Firmware structure One monolithic Arduino/PlatformIO app; maintainers confirm extracting just the mesh layer "would be a lot of refactoring" — RadioLib is the only cleanly reusable piece src/ is a genuine shared library (radio HAL, routing, crypto, identity) used by all role binaries (examples/{companion_radio,simple_repeater,...}) — more modular than Meshtastic RNode firmware (device) is a thin modem; all protocol logic lives in host-side RNS (Python) or the C++ port microReticulum (ESP32/nRF52, PlatformIO)
Documented host-bridge API ToRadio/FromRadio protobuf stream over Serial/BLE/TCP ("Client API") — official Python/JS/Android/iOS clients all use this Companion Radio protocol over BLE (Nordic UART-style) / USB serial, official JS (meshcore.js) + Python (meshcore_py) clients KISS-framed serial protocol (FEND/FESC escaping, command opcodes for freq/BW/SF/CR/TX/RX), same approach archy's existing reticulum.rs already validated against canonical source
Reference ESP32 boards Heltec V3, T-Beam (Supreme), Station G2 Same boards — Heltec V3, T-Beam, Station G2, XIAO+Wio-SX1262 Same boards again — T-Beam, Heltec V3 all RNode-flashable

Open items still unconfirmed (verify in source before relying on them): exact MeshCore sync word / RadioLib default; MeshCore's exact AES cipher mode (sources disagree: AES-128 vs AES-128-ECB vs AES+HMAC); whether any two protocols' current real-world default configs ever overlap enough for one static radio config to receive both.


2. The central constraint: one radio, three networks

The Heltec V3 has exactly one SX1262. A LoRa radio only demodulates a packet if it's currently configured with the matching frequency, bandwidth, spreading factor, and sync word. Since Meshtastic, MeshCore, and Reticulum each use independently-configured (and, per the table above, currently non-overlapping) PHY parameters, one radio cannot simultaneously listen to all three networks. This is true regardless of how clever the software is — it's a property of the radio hardware, not the firmware.

That leaves two real options for a single-board device:

A. Time-multiplexed single radio. Rotate the SX1262 through three PHY configs on a schedule (e.g. 1-2s dwell per network), queueing outbound traffic between windows. Pros: works on a stock, unmodified Heltec V3. Cons: it's a probabilistic repeater, not an always-on one — it will miss packets transmitted by other nodes while parked on a different network's config, and rebroadcast latency goes up on all three networks. Network participants assume their peers are always listening; a node that's only half the time will look unreliable to them (more so on networks like Meshtastic where missed CSMA windows + flood timing matter).

B. Add cheap radio breakouts. The ESP32-S3 has multiple SPI-capable GPIO pairs; 1-2 additional SX1262 breakout modules (~$5-10 each) wired to spare CS/IRQ/RESET lines turn the single Heltec V3 board into a host MCU with one dedicated radio per network, each always listening, no time- sharing. This keeps "one device" but is no longer a stock/unmodified board — it requires soldering/wiring. RAM (512KB, no PSRAM) is the harder limit here: running three protocols' framing/routing/crypto state concurrently as FreeRTOS tasks, lean builds will be required regardless (drop WiFi entirely, likely BLE too, cap NodeDB/path-table sizes).

Recommendation: start with A as the v1 proof of concept (cheapest, zero extra hardware, validates the bridging/translation logic), and treat B as the v2 hardware revision once the time-multiplexed version proves the software design is sound and someone decides duty-cycled coverage isn't good enough.


Phase 0 — De-risk the bridge logic, not the hardware squeeze. Before fighting RAM and single-radio limits at all, build the message-translation/repeater logic across three separate official-firmware boards (e.g. 3 spare ESP32 LoRa boards, one flashed stock Meshtastic, one stock MeshCore companion-radio, one RNode), each talking its own documented serial/BLE client API to a host (a Pi or PC). This validates "can we correctly receive a message on network A and re-originate/repeat it on networks B and C" without touching firmware internals or memory limits at all — pure host-side bridge code against three already-working radios.

Phase 1 — Single Heltec V3, time-multiplexed (Option A). Port the host-side bridge logic from Phase 0 onto the Heltec V3 itself, replacing "three separate official boards" with "one onboard SX1262 re-tuned on a schedule." This is where Meshtastic/MeshCore/RNode-equivalent framing+crypto needs to be reimplemented in lean, embeddable form (most realistic source for that: MeshCore's src/ since it's already a reusable library; microReticulum for the Reticulum side; for Meshtastic, RadioLib directly plus a minimal from-scratch reimplementation of just the 16-byte header/AES-CTR/dedup logic documented in ARCHITECTURE — not the full Meshtastic app).

Phase 2 — Multi-radio hardware revision (Option B), if duty-cycled coverage proves insufficient. Add dedicated radio breakouts so each network gets always-on reception; revisit RAM budget with leaner per- protocol implementations informed by what Phase 1 actually needed.


4. Why this is more tractable than "reimplement three mesh protocols"

All three ecosystems independently converged on the same pattern: don't reimplement the radio/routing stack — run the real firmware (or a real library) and talk to it over a documented protocol. That's the basis for Phase 0, and de-risks the project considerably versus a from-scratch clean- room implementation of three different mesh routing algorithms.

The harder, protocol-native reimplementation work (needed for Phase 1's single-radio squeeze, since there's no separate module to "talk to" — it's all one firmware image) is least risky for MeshCore (genuinely modular src/) and Reticulum (microReticulum already exists as a C++ port, though missing Link/Resource/Channel), and highest risk for Meshtastic (monolithic firmware, no officially separable mesh library — would mean reimplementing the 16-byte header / AES-CTR / PacketHistory dedup / SNR-weighted CSMA rebroadcast logic from spec, documented above, rather than reusing existing code).


5. Next steps

  • Confirm MeshCore's actual sync word / cipher mode against src/Utils.cpp and the RadioLib config (open item from §1)
  • Phase 0 prototype: pick 3 spare boards, flash stock firmware each, write host-side bridge translating one test message across all three
  • Decide on a minimal common internal message format for the bridge (sender id, origin network, payload, hop/path metadata) that all three protocol adapters translate to/from
  • Revisit RAM budget once Phase 1's actual per-protocol footprint is known