Add Phase 0 host-bridge: relay text across Meshtastic, MeshCore, Reticulum
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env python3
|
||||
"""archy-messh Phase 0 host-bridge.
|
||||
|
||||
Connects to one radio per network (Meshtastic, MeshCore, Reticulum/RNode),
|
||||
each over its official client protocol, and relays text messages between
|
||||
all three. See docs/ARCHITECTURE.md for why this is Phase 0 (prove the
|
||||
bridging logic on 3 separate boards) rather than Phase 1 (single
|
||||
time-multiplexed radio on one Heltec V3).
|
||||
|
||||
NOT YET TESTED END-TO-END — written without physical hardware attached to
|
||||
the dev machine. Needs Meshtastic + MeshCore + RNode-flashed boards to
|
||||
validate; each adapter's wire-level behavior is only as good as its
|
||||
upstream library's docs/examples (cited in each adapter's docstring/header).
|
||||
|
||||
Usage (see README.md for full option list):
|
||||
python3 main.py \\
|
||||
--meshtastic-port /dev/ttyUSB0 \\
|
||||
--meshcore-port /dev/ttyUSB1 \\
|
||||
--reticulum-storage ./rns-storage
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import time
|
||||
|
||||
from adapters.meshcore_adapter import MeshCoreAdapter
|
||||
from adapters.meshtastic_adapter import MeshtasticAdapter
|
||||
from adapters.reticulum_adapter import ReticulumAdapter
|
||||
from bridge_core import BridgeMessage, MessageBus
|
||||
|
||||
|
||||
def build_arg_parser() -> argparse.ArgumentParser:
|
||||
p = argparse.ArgumentParser(description="archy-messh Phase 0 tri-protocol bridge")
|
||||
p.add_argument("--meshtastic-port", help="Meshtastic serial device (e.g. /dev/ttyUSB0)")
|
||||
p.add_argument("--meshtastic-host", help="Meshtastic TCP host, instead of serial")
|
||||
p.add_argument("--meshcore-port", help="MeshCore serial device (e.g. /dev/ttyUSB1)")
|
||||
p.add_argument("--meshcore-host", help="MeshCore TCP host, instead of serial")
|
||||
p.add_argument("--meshcore-channel", type=int, default=0, help="MeshCore public channel index")
|
||||
p.add_argument(
|
||||
"--reticulum-storage", default="./rns-storage", help="LXMF/RNS storage directory"
|
||||
)
|
||||
p.add_argument(
|
||||
"--dedup-ttl", type=float, default=300.0, help="Seconds to suppress repeat-content loops"
|
||||
)
|
||||
return p
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = build_arg_parser().parse_args()
|
||||
|
||||
bus = MessageBus(dedup_ttl_seconds=args.dedup_ttl)
|
||||
|
||||
def on_message(network: str, sender: str, text: str) -> None:
|
||||
bus.publish(BridgeMessage(network=network, sender=sender, text=text, received_at=time.time()))
|
||||
|
||||
meshtastic = MeshtasticAdapter(on_message, port=args.meshtastic_port, host=args.meshtastic_host)
|
||||
meshcore = MeshCoreAdapter(
|
||||
on_message,
|
||||
port=args.meshcore_port,
|
||||
host=args.meshcore_host,
|
||||
channel_idx=args.meshcore_channel,
|
||||
)
|
||||
reticulum = ReticulumAdapter(on_message, storage_path=args.reticulum_storage)
|
||||
|
||||
adapters = {
|
||||
MeshtasticAdapter.NETWORK: meshtastic,
|
||||
MeshCoreAdapter.NETWORK: meshcore,
|
||||
ReticulumAdapter.NETWORK: reticulum,
|
||||
}
|
||||
|
||||
print("Connecting to Meshtastic...")
|
||||
meshtastic.connect()
|
||||
print("Connecting to MeshCore...")
|
||||
meshcore.connect()
|
||||
print("Connecting to Reticulum...")
|
||||
reticulum.connect()
|
||||
print("All three adapters connected. Bridging...")
|
||||
|
||||
try:
|
||||
while True:
|
||||
message = bus.get()
|
||||
if bus.is_duplicate(message.text):
|
||||
continue
|
||||
print(f"[{message.network}] {message.sender}: {message.text}")
|
||||
for network, adapter in adapters.items():
|
||||
if network == message.network:
|
||||
continue # don't echo back onto the network it came from
|
||||
try:
|
||||
adapter.send(f"[{message.network}/{message.sender}] {message.text}")
|
||||
except Exception as exc: # noqa: BLE001
|
||||
print(f" ! failed to relay onto {network}: {exc}", file=sys.stderr)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
finally:
|
||||
meshtastic.close()
|
||||
meshcore.close()
|
||||
reticulum.close()
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user