From 1b6db308c9984b8c816842d22a8cd140cf6ca3e3 Mon Sep 17 00:00:00 2001 From: Chad Attermann Date: Thu, 7 Mar 2024 15:15:44 -0700 Subject: [PATCH] Added support for fragmenting packets Packets larger the HW_MTU will be fragmented on transmit and reassembled on receive. --- RNS/Interfaces/MeshtasticInterface.py | 115 +++++++++++++++++++++++--- 1 file changed, 104 insertions(+), 11 deletions(-) diff --git a/RNS/Interfaces/MeshtasticInterface.py b/RNS/Interfaces/MeshtasticInterface.py index 150b7a5..5b5cf79 100644 --- a/RNS/Interfaces/MeshtasticInterface.py +++ b/RNS/Interfaces/MeshtasticInterface.py @@ -1,8 +1,8 @@ from .Interface import Interface from pubsub import pub -import time -import sys +import os +import struct import RNS try: @@ -11,6 +11,9 @@ try: class MeshtasticInterface(Interface): + FLAG_SPLIT = 0x01 + SEQ_UNSET = 0xFF + @staticmethod def onReceive(packet, interface): if packet['decoded']['portnum'] == "PRIVATE_APP": @@ -38,6 +41,10 @@ try: self.bitrate = 5469 self.bitrate_kbps = 5.47 + self.buffer = None + self.sequence = 0xFF + self.ready = False + self.interface = None try: @@ -62,20 +69,106 @@ try: RNS.log("Initialized Meshtastic interface!", RNS.LOG_DEBUG) def processIncoming(self, data): - RNS.log("Received Meshtastic packet "+RNS.prettyhexrep(data), RNS.LOG_DEBUG) + RNS.log("Received Meshtastic packet of "+str(len(data))+" bytes: "+RNS.prettyhexrep(data), RNS.LOG_DEBUG) + self.rxb += len(data) - self.owner.inbound(data, self) + + # check for fragmented packet + header = data[0] + split = (header & self.FLAG_SPLIT) == self.FLAG_SPLIT + sequence = header >> 1 + RNS.log("incoming sequence: "+str(sequence), RNS.LOG_DEBUG) + + if split and self.sequence == self.SEQ_UNSET: + # This is the first part of a split + # packet, so we set the self.sequence variable + # and add the data to the buffer. + self.buffer = data[1:] + self.sequence = sequence + + #last_rssi = LoRa.packetRssi(); + #last_snr_raw = LoRa.packetSnrRaw(); + + elif split and self.sequence == sequence: + # This is subsequent part of a split + # packet, so we add it to the buffer. + self.buffer += data[1:] + + #last_rssi = (last_rssi+LoRa.packetRssi())/2; + #last_snr_raw = (last_snr_raw+LoRa.packetSnrRaw())/2; + + elif not split and self.sequence == sequence: + # This is not a split packet but it carries + # the last sequence id, so it must be the + # last part of a split packet. + self.buffer += data[1:] + self.sequence = self.SEQ_UNSET + + #last_rssi = LoRa.packetRssi(); + #last_snr_raw = LoRa.packetSnrRaw(); + + # pass the buffer on to RNS + RNS.log("Received Reticulum packet of "+str(len(self.buffer))+" bytes over Meshtastic interface", RNS.LOG_DEBUG) + self.owner.inbound(self.buffer, self) + self.buffer = None + + elif split and self.sequence != sequence: + # This split packet does not carry the + # same sequence id, so we must assume + # that we are seeing the first part of + # a new split packet. + # If we already had part of a split + # packet in the buffer then we clear it. + if self.buffer != None: + RNS.log("Discarding incomplete buffer from previous split packet!", RNS.LOG_ERROR) + self.buffer = data[1:] + self.sequence = sequence + + #last_rssi = LoRa.packetRssi(); + #last_snr_raw = LoRa.packetSnrRaw(); + + elif not split: + # This is not a split packet, so we + # just read it whole + self.buffer = data[1:] + self.sequence = self.SEQ_UNSET + + #last_rssi = LoRa.packetRssi(); + #last_snr_raw = LoRa.packetSnrRaw(); + + # pass the buffer on to RNS + RNS.log("Received Reticulum packet of "+str(len(self.buffer))+" bytes over Meshtastic interface", RNS.LOG_DEBUG) + self.owner.inbound(self.buffer, self) + self.buffer = None 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) + RNS.log("Sending Reticulum packet of "+str(len(data))+" bytes over Meshtastic interface", RNS.LOG_DEBUG) + + header = int.from_bytes(os.urandom(1), "big") & 0xFE + RNS.log("outgoing sequence: "+str(header >> 1), RNS.LOG_DEBUG) + if len(data) > self.HW_MTU-1: + header |= self.FLAG_SPLIT + + sent = 0 + while sent < len(data): + size = len(data) - sent + if size > self.HW_MTU-1: + size = self.HW_MTU-1 + # clear split flag for last part + if sent+size >= len(data): + header &= ~self.FLAG_SPLIT + buffer = struct.pack("!B", header) + data[sent:sent+size] + sent += size + + RNS.log("Sending Meshtastic packet of "+str(len(buffer))+" bytes: "+RNS.prettyhexrep(buffer), RNS.LOG_DEBUG) + try: + #self.interface.sendData(buffer) + self.interface.sendData(buffer, portNum=256, channelIndex=self.channel) + self.txb += len(buffer) + 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: