From ebd77a6c7d5b83658716b4bd31ed432822b62621 Mon Sep 17 00:00:00 2001 From: Chad Attermann Date: Mon, 4 Mar 2024 14:00:24 -0700 Subject: [PATCH 1/2] Very basic Meshtastic tunneling interface --- RNS/Interfaces/MeshtasticInterface.py | 67 +++++++++++++++++++++++++++ RNS/Reticulum.py | 27 +++++++++++ 2 files changed, 94 insertions(+) create mode 100644 RNS/Interfaces/MeshtasticInterface.py diff --git a/RNS/Interfaces/MeshtasticInterface.py b/RNS/Interfaces/MeshtasticInterface.py new file mode 100644 index 0000000..377358a --- /dev/null +++ b/RNS/Interfaces/MeshtasticInterface.py @@ -0,0 +1,67 @@ + +from .Interface import Interface +import meshtastic +import meshtastic.serial_interface +from pubsub import pub +import time +import sys +import RNS + + +class MeshtasticInterface(Interface): + + @staticmethod + def onReceive(packet, interface): + #print(f"Received: {packet}") + if packet['decoded']['portnum'] == "PRIVATE_APP": + interface.callback.processIncoming(packet['decoded']['payload']) + + def __init__(self, owner, name, port=None, channel=0): + super().__init__() + + self.rxb = 0 + self.txb = 0 + + self.HW_MTU = 256 + + self.IN = True + self.OUT = False + self.owner = owner + self.name = name + self.port = port + self.channel = channel + self.online = False + # static default long-fast bitrate + self.bitrate = 5469 + self.bitrate_kbps = 5.47 + + RNS.log("Initializing Meshtastic interface...", RNS.LOG_DEBUG) + self.interface = meshtastic.serial_interface.SerialInterface(devPath=port) + self.interface.callback = self + + RNS.log("Subscribing to Meshtastic events", RNS.LOG_DEBUG) + pub.subscribe(self.onReceive, "meshtastic.receive.data") + + RNS.log("Initialized Meshtastic interface!", RNS.LOG_DEBUG) + + def processIncoming(self, data): + RNS.log("Received Meshtastic packet "+RNS.prettyhexrep(data), RNS.LOG_DEBUG) + self.rxb += len(data) + self.owner.inbound(data, self) + + def processOutgoing(self, data): + RNS.log("Sending Meshtastic packet "+RNS.prettyhexrep(data), RNS.LOG_DEBUG) + try: + #self.interface.sendData(data) + self.interface.sendData(data, portNum=256, channelIndex=self.channel) + self.txb += len(data) + + except Exception as e: + RNS.log("Could not transmit on "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) + + + def __str__(self): + if self.port == None: + return "MeshtasticInterface["+self.name+"]" + return "MeshtasticInterface["+self.name+"/"+self.port+"]" + diff --git a/RNS/Reticulum.py b/RNS/Reticulum.py index a764e43..8db7d9b 100755 --- a/RNS/Reticulum.py +++ b/RNS/Reticulum.py @@ -933,6 +933,33 @@ class Reticulum: else: interface.ifac_size = 8 + if c["type"] == "MeshtasticInterface": + + port = c["port"] if "port" in c else None + channel = int(c["channel"]) if "channel" in c else 0 + + interface = MeshtasticInterface.MeshtasticInterface( + RNS.Transport, + name, + port, + channel + ) + + if "outgoing" in c and c.as_bool("outgoing") == False: + interface.OUT = False + else: + interface.OUT = True + + interface.mode = interface_mode + + interface.announce_cap = announce_cap + if configured_bitrate: + interface.bitrate = configured_bitrate + if ifac_size != None: + interface.ifac_size = ifac_size + else: + interface.ifac_size = 8 + if interface != None: interface.announce_rate_target = announce_rate_target interface.announce_rate_grace = announce_rate_grace From 2c67ae9d8bf7070e2675f2638ab050c3e60165f1 Mon Sep 17 00:00:00 2001 From: Chad Attermann Date: Wed, 6 Mar 2024 14:52:04 -0700 Subject: [PATCH 2/2] Enhanced error handling --- RNS/Interfaces/MeshtasticInterface.py | 127 ++++++++++++++++---------- RNS/Reticulum.py | 6 +- 2 files changed, 82 insertions(+), 51 deletions(-) diff --git a/RNS/Interfaces/MeshtasticInterface.py b/RNS/Interfaces/MeshtasticInterface.py index 377358a..9ad97a0 100644 --- a/RNS/Interfaces/MeshtasticInterface.py +++ b/RNS/Interfaces/MeshtasticInterface.py @@ -1,67 +1,102 @@ from .Interface import Interface -import meshtastic -import meshtastic.serial_interface from pubsub import pub import time import sys import RNS +try: + import meshtastic + import meshtastic.serial_interface -class MeshtasticInterface(Interface): + class MeshtasticInterface(Interface): - @staticmethod - def onReceive(packet, interface): - #print(f"Received: {packet}") - if packet['decoded']['portnum'] == "PRIVATE_APP": - interface.callback.processIncoming(packet['decoded']['payload']) + @staticmethod + def onReceive(packet, interface): + if packet['decoded']['portnum'] == "PRIVATE_APP": + interface.callback.processIncoming(packet['decoded']['payload']) - def __init__(self, owner, name, port=None, channel=0): - super().__init__() + def __init__(self, owner, name, config): + super().__init__() - self.rxb = 0 - self.txb = 0 + self.port = config["port"] if "port" in config else None + RNS.log("MeshtasticInterface: port="+str(self.port), RNS.LOG_DEBUG) + self.channel = int(config["channel"]) if "channel" in config else 0 + RNS.log("MeshtasticInterface: channel="+str(self.channel), RNS.LOG_DEBUG) - self.HW_MTU = 256 + self.rxb = 0 + self.txb = 0 - self.IN = True - self.OUT = False - self.owner = owner - self.name = name - self.port = port - self.channel = channel - self.online = False - # static default long-fast bitrate - self.bitrate = 5469 - self.bitrate_kbps = 5.47 + self.HW_MTU = 256 - RNS.log("Initializing Meshtastic interface...", RNS.LOG_DEBUG) - self.interface = meshtastic.serial_interface.SerialInterface(devPath=port) - self.interface.callback = self + self.IN = True + self.OUT = False + self.owner = owner + self.name = name + self.online = False + # static default long-fast bitrate + self.bitrate = 5469 + self.bitrate_kbps = 5.47 - RNS.log("Subscribing to Meshtastic events", RNS.LOG_DEBUG) - pub.subscribe(self.onReceive, "meshtastic.receive.data") + self.interface = None - RNS.log("Initialized Meshtastic interface!", RNS.LOG_DEBUG) + try: + RNS.log("Initializing Meshtastic interface...", RNS.LOG_DEBUG) + # avoid api forcing app to terminate when no devices are found + if self.port == None: + ports = meshtastic.util.findPorts(True) + if len(ports) == 0: + RNS.log("Failed to initialize Meshtastic interface! No devices found", RNS.LOG_ERROR) + return + self.interface = meshtastic.serial_interface.SerialInterface(devPath=self.port) + self.interface.callback = self - def processIncoming(self, data): - RNS.log("Received Meshtastic packet "+RNS.prettyhexrep(data), RNS.LOG_DEBUG) - self.rxb += len(data) - self.owner.inbound(data, self) + RNS.log("Subscribing to Meshtastic events", RNS.LOG_DEBUG) + pub.subscribe(self.onReceive, "meshtastic.receive.data") - def processOutgoing(self, data): - RNS.log("Sending Meshtastic packet "+RNS.prettyhexrep(data), RNS.LOG_DEBUG) - try: - #self.interface.sendData(data) - self.interface.sendData(data, portNum=256, channelIndex=self.channel) - self.txb += len(data) - - except Exception as e: - RNS.log("Could not transmit on "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) + except Exception as e: + RNS.log("Failed to initialize Meshtastic interface! "+str(e), RNS.LOG_ERROR) + self.interface = None + return + RNS.log("Initialized Meshtastic interface!", RNS.LOG_DEBUG) - def __str__(self): - if self.port == None: - return "MeshtasticInterface["+self.name+"]" - return "MeshtasticInterface["+self.name+"/"+self.port+"]" + def processIncoming(self, data): + RNS.log("Received Meshtastic packet "+RNS.prettyhexrep(data), RNS.LOG_DEBUG) + self.rxb += len(data) + self.owner.inbound(data, self) + + def processOutgoing(self, data): + if self.interface == None: + return + RNS.log("Sending Meshtastic packet "+RNS.prettyhexrep(data), RNS.LOG_DEBUG) + try: + #self.interface.sendData(data) + self.interface.sendData(data, portNum=256, channelIndex=self.channel) + self.txb += len(data) + except Exception as e: + RNS.log("Could not transmit on "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) + + def __str__(self): + if self.port == None: + return "MeshtasticInterface["+self.name+"]" + return "MeshtasticInterface["+self.name+"/"+self.port+"]" + +except ImportError: + + class MeshtasticInterface(Interface): + + def __init__(self, owner, name, port=None, channel=0): + super().__init__() + + RNS.log("Failed to initialize Meshtastic interface! Meshtastic API is not installed", RNS.LOG_ERROR) + #raise OSError("Meshtastic API is not installed") + + def processOutgoing(self, data): + return + + def __str__(self): + if self.port == None: + return "MeshtasticInterface["+self.name+"]" + return "MeshtasticInterface["+self.name+"/"+self.port+"]" diff --git a/RNS/Reticulum.py b/RNS/Reticulum.py index 8db7d9b..a37b1a6 100755 --- a/RNS/Reticulum.py +++ b/RNS/Reticulum.py @@ -935,14 +935,10 @@ class Reticulum: if c["type"] == "MeshtasticInterface": - port = c["port"] if "port" in c else None - channel = int(c["channel"]) if "channel" in c else 0 - interface = MeshtasticInterface.MeshtasticInterface( RNS.Transport, name, - port, - channel + c ) if "outgoing" in c and c.as_bool("outgoing") == False: