Implemented ID beaconing on RNode and KISS interfaces

This commit is contained in:
Mark Qvist 2020-08-13 15:06:39 +02:00
parent 88a956b4f5
commit f275065b40
4 changed files with 81 additions and 22 deletions

View File

@ -39,7 +39,7 @@ class KISSInterface(Interface):
stopbits = None stopbits = None
serial = None serial = None
def __init__(self, owner, name, port, speed, databits, parity, stopbits, preamble, txtail, persistence, slottime, flow_control): def __init__(self, owner, name, port, speed, databits, parity, stopbits, preamble, txtail, persistence, slottime, flow_control, beacon_interval, beacon_data):
self.serial = None self.serial = None
self.owner = owner self.owner = owner
self.name = name self.name = name
@ -50,10 +50,15 @@ class KISSInterface(Interface):
self.stopbits = stopbits self.stopbits = stopbits
self.timeout = 100 self.timeout = 100
self.online = False self.online = False
self.beacon_i = beacon_interval
self.beacon_d = beacon_data.encode("utf-8")
self.first_tx = None
self.packet_queue = [] self.packet_queue = []
self.flow_control = flow_control self.flow_control = flow_control
self.interface_ready = False self.interface_ready = False
self.flow_control_timeout = 10
self.flow_control_locked = time.time()
self.preamble = preamble if preamble != None else 350; self.preamble = preamble if preamble != None else 350;
self.txtail = txtail if txtail != None else 20; self.txtail = txtail if txtail != None else 20;
@ -174,12 +179,20 @@ class KISSInterface(Interface):
if self.interface_ready: if self.interface_ready:
if self.flow_control: if self.flow_control:
self.interface_ready = False self.interface_ready = False
self.flow_control_locked = time.time()
data = data.replace(bytes([0xdb]), bytes([0xdb])+bytes([0xdd])) data = data.replace(bytes([0xdb]), bytes([0xdb])+bytes([0xdd]))
data = data.replace(bytes([0xc0]), bytes([0xdb])+bytes([0xdc])) data = data.replace(bytes([0xc0]), bytes([0xdb])+bytes([0xdc]))
frame = bytes([KISS.FEND])+bytes([0x00])+data+bytes([KISS.FEND]) frame = bytes([KISS.FEND])+bytes([0x00])+data+bytes([KISS.FEND])
written = self.serial.write(frame) written = self.serial.write(frame)
if data == self.beacon_d:
self.first_tx = None
else:
if self.first_tx == None:
self.first_tx = time.time()
if written != len(frame): if written != len(frame):
raise IOError("Serial interface only wrote "+str(written)+" bytes of "+str(len(data))) raise IOError("Serial interface only wrote "+str(written)+" bytes of "+str(len(data)))
@ -235,8 +248,6 @@ class KISSInterface(Interface):
escape = False escape = False
data_buffer = data_buffer+bytes([byte]) data_buffer = data_buffer+bytes([byte])
elif (command == KISS.CMD_READY): elif (command == KISS.CMD_READY):
# TODO: add timeout and reset if ready
# command never arrives
self.process_queue() self.process_queue()
else: else:
time_since_last = int(time.time()*1000) - last_read_ms time_since_last = int(time.time()*1000) - last_read_ms
@ -247,6 +258,19 @@ class KISSInterface(Interface):
escape = False escape = False
sleep(0.08) sleep(0.08)
if self.flow_control:
if not self.interface_ready:
if time.time() > self.flow_control_locked + self.flow_control_timeout:
RNS.log("Interface "+str(self)+" is unlocking flow control due to time-out. This should not happen. Your hardware might have missed a flow-control READY command.", RNS.LOG_WARNING)
self.process_queue()
if self.beacon_i != None and self.beacon_d != None:
if self.first_tx != None:
if time.time() > self.first_tx + self.beacon_i:
RNS.log("Interface "+str(self)+" is transmitting beacon data: "+str(self.beacon_d.decode("utf-8")), RNS.LOG_DEBUG)
self.first_tx = None
self.processOutgoing(self.beacon_d)
except Exception as e: except Exception as e:
self.online = False self.online = False
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)

View File

@ -92,6 +92,7 @@ class RNodeInterface(Interface):
self.bitrate = 0 self.bitrate = 0
self.last_id = 0 self.last_id = 0
self.first_tx = None
self.r_frequency = None self.r_frequency = None
self.r_bandwidth = None self.r_bandwidth = None
@ -133,7 +134,7 @@ class RNodeInterface(Interface):
if id_interval != None and id_callsign != None: if id_interval != None and id_callsign != None:
if (len(id_callsign.encode("utf-8")) <= RNodeInterface.CALLSIGN_MAX_LEN): if (len(id_callsign.encode("utf-8")) <= RNodeInterface.CALLSIGN_MAX_LEN):
self.should_id = True self.should_id = True
self.id_callsign = id_callsign self.id_callsign = id_callsign.encode("utf-8")
self.id_interval = id_interval self.id_interval = id_interval
else: else:
RNS.log("The encoded ID callsign for "+str(self)+" exceeds the max length of "+str(RNodeInterface.CALLSIGN_MAX_LEN)+" bytes.", RNS.LOG_ERROR) RNS.log("The encoded ID callsign for "+str(self)+" exceeds the max length of "+str(RNodeInterface.CALLSIGN_MAX_LEN)+" bytes.", RNS.LOG_ERROR)
@ -286,15 +287,15 @@ class RNodeInterface(Interface):
if self.flow_control: if self.flow_control:
self.interface_ready = False self.interface_ready = False
frame = b"" if data == self.id_callsign:
self.first_tx = None
if self.id_interval != None and self.id_callsign != None: else:
if self.last_id + self.id_interval < time.time(): if self.first_tx == None:
self.last_id = time.time() self.first_tx = time.time()
frame = bytes([0xc0])+bytes([0x00])+KISS.escape(self.id_callsign.encode("utf-8"))+bytes([0xc0])
data = KISS.escape(data) data = KISS.escape(data)
frame += bytes([0xc0])+bytes([0x00])+data+bytes([0xc0]) frame = bytes([0xc0])+bytes([0x00])+data+bytes([0xc0])
written = self.serial.write(frame) written = self.serial.write(frame)
if written != len(frame): if written != len(frame):
@ -450,6 +451,13 @@ class RNodeInterface(Interface):
in_frame = False in_frame = False
command = KISS.CMD_UNKNOWN command = KISS.CMD_UNKNOWN
escape = False escape = False
if self.id_interval != None and self.id_callsign != None:
if self.first_tx != None:
if time.time() > self.first_tx + self.id_interval:
RNS.log("Interface "+str(self)+" is transmitting beacon data: "+str(self.id_callsign.decode("utf-8")), RNS.LOG_DEBUG)
self.processOutgoing(self.id_callsign)
sleep(0.08) sleep(0.08)
except Exception as e: except Exception as e:

View File

@ -256,6 +256,8 @@ class Reticulum:
databits = int(c["databits"]) if "databits" in c else 8 databits = int(c["databits"]) if "databits" in c else 8
parity = c["parity"] if "parity" in c else "N" parity = c["parity"] if "parity" in c else "N"
stopbits = int(c["stopbits"]) if "stopbits" in c else 1 stopbits = int(c["stopbits"]) if "stopbits" in c else 1
beacon_interval = int(c["id_interval"]) if "id_interval" in c else None
beacon_data = c["id_callsign"] if "id_callsign" in c else None
if port == None: if port == None:
raise ValueError("No port specified for serial interface") raise ValueError("No port specified for serial interface")
@ -272,7 +274,9 @@ class Reticulum:
txtail, txtail,
persistence, persistence,
slottime, slottime,
flow_control flow_control,
beacon_interval,
beacon_data
) )
if "outgoing" in c and c["outgoing"].lower() == "true": if "outgoing" in c and c["outgoing"].lower() == "true":
@ -542,15 +546,21 @@ loglevel = 4
# out identification on the channel with # out identification on the channel with
# a set interval by configuring the # a set interval by configuring the
# following two parameters. The trans- # following two parameters. The trans-
# ceiver will only ID before making an # ceiver will only ID if the set
# actual transmission, and if the set
# interval has elapsed since it's last # interval has elapsed since it's last
# ID. Interval is configured in seconds # actual transmission. The interval is
# configured in seconds.
# This option is commented out and not # This option is commented out and not
# used by default. # used by default.
# id_callsign = MYCALL-0 # id_callsign = MYCALL-0
# id_interval = 600 # id_interval = 600
# For certain homebrew RNode interfaces
# with low amounts of RAM, using packet
# flow control can be useful. By default
# it is disabled.
flow_control = False
# An example KISS modem interface. Useful for running # An example KISS modem interface. Useful for running
# Reticulum over packet radio hardware. # Reticulum over packet radio hardware.
@ -574,11 +584,6 @@ loglevel = 4
parity = none parity = none
stopbits = 1 stopbits = 1
# Whether to use KISS flow-control.
# This is useful for modems with a
# small internal packet buffer.
flow_control = false
# Set the modem preamble. A 150ms # Set the modem preamble. A 150ms
# preamble should be a reasonable # preamble should be a reasonable
# default, but may need to be # default, but may need to be
@ -597,6 +602,25 @@ loglevel = 4
persistence = 200 persistence = 200
slottime = 20 slottime = 20
# You can configure the interface to send
# out identification on the channel with
# a set interval by configuring the
# following two parameters. The KISS
# interface will only ID if the set
# interval has elapsed since it's last
# actual transmission. The interval is
# configured in seconds.
# This option is commented out and not
# used by default.
# id_callsign = MYCALL-0
# id_interval = 600
# Whether to use KISS flow-control.
# This is useful for modems that have
# a small internal packet buffer, but
# support packet flow control instead.
flow_control = false
# If you're using Reticulum on amateur radio spectrum, # If you're using Reticulum on amateur radio spectrum,
# you might want to use the AX.25 KISS interface. This # you might want to use the AX.25 KISS interface. This
@ -607,6 +631,9 @@ loglevel = 4
# Only do this if you really need to! Reticulum doesn't # Only do this if you really need to! Reticulum doesn't
# need the AX.25 layer for anything, and it incurs extra # need the AX.25 layer for anything, and it incurs extra
# overhead on every packet to encapsulate in AX.25. # overhead on every packet to encapsulate in AX.25.
#
# A more efficient way is to use the plain KISS interface
# with the beaconing functionality described above.
[[Packet Radio AX.25 KISS Interface]] [[Packet Radio AX.25 KISS Interface]]
type = AX25KISSInterface type = AX25KISSInterface

View File

@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
setuptools.setup( setuptools.setup(
name="rns", name="rns",
version="0.1.7", version="0.1.8",
author="Mark Qvist", author="Mark Qvist",
author_email="mark@unsigned.io", author_email="mark@unsigned.io",
description="Self-configuring, encrypted and resilient mesh networking stack for LoRa, packet radio, WiFi and everything in between", description="Self-configuring, encrypted and resilient mesh networking stack for LoRa, packet radio, WiFi and everything in between",