Implemented ID beaconing on RNode and KISS interfaces
This commit is contained in:
parent
88a956b4f5
commit
f275065b40
@ -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)
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
2
setup.py
2
setup.py
@ -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",
|
||||||
|
Loading…
Reference in New Issue
Block a user