From 50022e1e9eb863f5b53c4d8067baff9a94e631ee Mon Sep 17 00:00:00 2001 From: Walter Boring Date: Wed, 26 Nov 2025 16:19:37 -0500 Subject: [PATCH] Got the serial KISS driver working --- aprsd/client/drivers/__init__.py | 2 + aprsd/client/drivers/kiss_common.py | 5 ++- aprsd/client/drivers/serialkiss.py | 61 ++++++++++++++++++++--------- aprsd/client/drivers/tcpkiss.py | 2 - 4 files changed, 48 insertions(+), 22 deletions(-) diff --git a/aprsd/client/drivers/__init__.py b/aprsd/client/drivers/__init__.py index 8da8326..92cc247 100644 --- a/aprsd/client/drivers/__init__.py +++ b/aprsd/client/drivers/__init__.py @@ -2,9 +2,11 @@ from aprsd.client.drivers.aprsis import APRSISDriver from aprsd.client.drivers.fake import APRSDFakeDriver from aprsd.client.drivers.registry import DriverRegistry +from aprsd.client.drivers.serialkiss import SerialKISSDriver from aprsd.client.drivers.tcpkiss import TCPKISSDriver driver_registry = DriverRegistry() driver_registry.register(APRSDFakeDriver) driver_registry.register(APRSISDriver) driver_registry.register(TCPKISSDriver) +driver_registry.register(SerialKISSDriver) diff --git a/aprsd/client/drivers/kiss_common.py b/aprsd/client/drivers/kiss_common.py index 6fe9069..4a5308e 100644 --- a/aprsd/client/drivers/kiss_common.py +++ b/aprsd/client/drivers/kiss_common.py @@ -27,6 +27,9 @@ class KISSDriver(metaclass=trace.TraceWrapperMetaclass): last_packet_received = None keepalive = None + # timeout in seconds + select_timeout = 1 + def __init__(self): """Initialize the KISS client. @@ -85,7 +88,6 @@ class KISSDriver(metaclass=trace.TraceWrapperMetaclass): frame = kissutil.recover_special_codes(kissutil.strip_nmea(bytes(buffer))) if strip_df_start: frame = kissutil.strip_df_start(frame) - LOG.warning(f'handle_fend {" ".join(f"{b:02X}" for b in bytes(frame))}') return bytes(frame) def fix_raw_frame(self, raw_frame: bytes) -> bytes: @@ -145,6 +147,7 @@ class KISSDriver(metaclass=trace.TraceWrapperMetaclass): """ raise NotImplementedError('read_frame is not implemented for KISS') + @trace.no_trace def stats(self, serializable: bool = False) -> Dict[str, Any]: """Get client statistics. diff --git a/aprsd/client/drivers/serialkiss.py b/aprsd/client/drivers/serialkiss.py index 0855f34..5fe1aee 100644 --- a/aprsd/client/drivers/serialkiss.py +++ b/aprsd/client/drivers/serialkiss.py @@ -7,7 +7,8 @@ non-asyncio KISSInterface implementation. import datetime import logging -import select + +# import select from typing import Any, Dict import serial @@ -24,6 +25,7 @@ from aprsd import ( # noqa ) from aprsd.client.drivers.kiss_common import KISSDriver from aprsd.packets import core +from aprsd.utils import trace CONF = cfg.CONF LOG = logging.getLogger('APRSD') @@ -80,6 +82,11 @@ class SerialKISSDriver(KISSDriver): def close(self): """Close the connection.""" self._connected = False + if self.socket and self.socket.is_open: + try: + self.socket.close() + except Exception: + pass def setup_connection(self): """Set up the KISS interface.""" @@ -118,11 +125,26 @@ class SerialKISSDriver(KISSDriver): LOG.warning('KISS interface already connected') return + # Close existing socket if it exists + if self.socket and self.socket.is_open: + try: + self.socket.close() + except Exception: + pass + try: + # serial.Serial() automatically opens the port, so we don't need to call open() self.socket = serial.Serial( - CONF.kiss_serial.device, CONF.kiss_serial.baudrate + CONF.kiss_serial.device, + timeout=1, + baudrate=CONF.kiss_serial.baudrate, + # bytesize=8, + # parity='N', + # stopbits=1, + # xonxoff=False, + # rtscts=False, + # dsrdtr=False, ) - self.socket.open() self._connected = True except serial.SerialException as e: LOG.error(f'Failed to connect to KISS interface: {e}') @@ -142,21 +164,21 @@ class SerialKISSDriver(KISSDriver): return None while self._connected: - try: - readable, _, _ = select.select( - [self.socket], - [], - [], - self.select_timeout, - ) - if not readable: - continue - except Exception as e: - # No need to log if we are not running. - # this happens when the client is stopped/closed. - LOG.error(f'Error in read loop: {e}') - self._connected = False - break + # try: + # readable, _, _ = select.select( + # [self.socket], + ## [], + # [], + # self.select_timeout, + # ) + # if not readable: + # continue + # except Exception as e: + # # No need to log if we are not running. + # # this happens when the client is stopped/closed. + # LOG.error(f'Error in read loop: {e}') + # self._connected = False + # break try: short_buf = self.socket.read(1024) @@ -208,12 +230,13 @@ class SerialKISSDriver(KISSDriver): frame_kiss = b''.join( [kiss_constants.FEND, command.value, frame_escaped, kiss_constants.FEND] ) - self.socket.send(frame_kiss) + self.socket.write(frame_kiss) # Update last packet sent time self.last_packet_sent = datetime.datetime.now() # Increment packets sent counter self.packets_sent += 1 + @trace.no_trace def stats(self, serializable: bool = False) -> Dict[str, Any]: """Get client statistics. diff --git a/aprsd/client/drivers/tcpkiss.py b/aprsd/client/drivers/tcpkiss.py index 8434426..ddc2f90 100644 --- a/aprsd/client/drivers/tcpkiss.py +++ b/aprsd/client/drivers/tcpkiss.py @@ -39,8 +39,6 @@ class TCPKISSDriver(KISSDriver): # Class level attributes required by Client protocol client_name = None socket = None - # timeout in seconds - select_timeout = 1 path = None def __new__(cls, *args, **kwargs):