diff --git a/.pylintrc b/.pylintrc index c7e6475..5deb236 100644 --- a/.pylintrc +++ b/.pylintrc @@ -23,7 +23,7 @@ ignore-patterns=mqtt_pb2.py,channel_pb2.py,environmental_measurement_pb2.py,admi # no Warning level messages displayed, use"--disable=all --enable=classes # --disable=W" # -disable=invalid-name,fixme,logging-fstring-interpolation,too-many-statements,too-many-branches,too-many-locals,no-member,f-string-without-interpolation,protected-access,no-self-use,pointless-string-statement,too-few-public-methods,consider-using-f-string,broad-except,no-else-return,unused-argument,global-statement,global-variable-not-assigned,too-many-boolean-expressions,no-else-raise,bare-except +disable=invalid-name,fixme,logging-fstring-interpolation,too-many-statements,too-many-branches,too-many-locals,no-member,f-string-without-interpolation,protected-access,no-self-use,pointless-string-statement,too-few-public-methods,consider-using-f-string,broad-except,no-else-return,unused-argument,global-statement,global-variable-not-assigned,too-many-boolean-expressions,no-else-raise,bare-except,c-extension-no-member [BASIC] diff --git a/meshtastic/serial_interface.py b/meshtastic/serial_interface.py index 90b6922..464ba82 100644 --- a/meshtastic/serial_interface.py +++ b/meshtastic/serial_interface.py @@ -1,9 +1,8 @@ """ Serial interface class """ import logging -import platform -import os -import stat +import time +import termios import serial import meshtastic.util @@ -20,6 +19,7 @@ class SerialInterface(StreamInterface): devPath {string} -- A filepath to a device, i.e. /dev/ttyUSB0 (default: {None}) debugOut {stream} -- If a stream is provided, any debug serial output from the device will be emitted to that stream. (default: {None}) """ + self.noProto = noProto if devPath is None: ports = meshtastic.util.findPorts() @@ -35,43 +35,29 @@ class SerialInterface(StreamInterface): logging.debug(f"Connecting to {devPath}") - # Note: we provide None for port here, because we will be opening it later - self.stream = serial.Serial( - None, 921600, exclusive=True, timeout=0.5, write_timeout=0) + # first we need to set the HUPCL so the device will not reboot based on RTS and/or DTR + # see https://github.com/pyserial/pyserial/issues/124 + if not self.noProto: + with open(devPath, encoding='utf8') as f: + attrs = termios.tcgetattr(f) + attrs[2] = attrs[2] & ~termios.HUPCL + termios.tcsetattr(f, termios.TCSAFLUSH, attrs) + f.close() + time.sleep(0.1) - # rts=False Needed to prevent TBEAMs resetting on OSX, because rts is connected to reset - self.stream.port = devPath + self.stream = serial.Serial(devPath, 921600, exclusive=True, timeout=0.5) + if not self.noProto: + self.stream.flush() + time.sleep(0.1) - # HACK: If the platform driving the serial port is unable to leave the RTS pin in high-impedance - # mode, set RTS to false so that the device platform won't be reset spuriously. - # Linux does this properly, so don't apply this hack on Linux (because it makes the reset button not work). - if self._hostPlatformAlwaysDrivesUartRts(): - self.stream.rts = False - self.stream.open() + StreamInterface.__init__(self, debugOut=debugOut, noProto=noProto, connectNow=connectNow) - StreamInterface.__init__( - self, debugOut=debugOut, noProto=noProto, connectNow=connectNow) - - """true if platform driving the serial port is Windows Subsystem for Linux 1.""" - def _isWsl1(self): - # WSL1 identifies itself as Linux, but has a special char device at /dev/lxss for use with session control, - # e.g. /init. We should treat WSL1 as Windows for the RTS-driving hack because the underlying platfrom - # serial driver for the CP21xx still exhibits the buggy behavior. - # WSL2 is not covered here, as it does not (as of 2021-May-25) support the appropriate functionality to - # share or pass-through serial ports. - try: - # Claims to be Linux, but has /dev/lxss; must be WSL 1 - return platform.system() == 'Linux' and stat.S_ISCHR(os.stat('/dev/lxss').st_mode) - except: - # Couldn't stat /dev/lxss special device; not WSL1 - return False - - def _hostPlatformAlwaysDrivesUartRts(self): - # OS-X/Windows seems to have a bug in its CP21xx serial drivers. It ignores that we asked for no RTSCTS - # control and will always drive RTS either high or low (rather than letting the CP102 leave - # it as an open-collector floating pin). - # TODO: When WSL2 supports USB passthrough, this will get messier. If/when WSL2 gets virtual serial - # ports that "share" the Windows serial port (and thus the Windows drivers), this code will need to be - # updated to reflect that as well -- or if T-Beams get made with an alternate USB to UART bridge that has - # a less buggy driver. - return platform.system() != 'Linux' or self._isWsl1() + def close(self): + """Close a connection to the device""" + if not self.noProto: + self.stream.flush() + time.sleep(0.1) + self.stream.flush() + time.sleep(0.1) + logging.debug("Closing Serial stream") + StreamInterface.close(self) diff --git a/meshtastic/stream_interface.py b/meshtastic/stream_interface.py index a3c20b5..8b81f9c 100644 --- a/meshtastic/stream_interface.py +++ b/meshtastic/stream_interface.py @@ -62,7 +62,8 @@ class StreamInterface(MeshInterface): # because we want to ensure it is looking for START1) p = bytearray([START2] * 32) self._writeBytes(p) - time.sleep(0.1) # wait 100ms to give device time to start running + if not self.noProto: + time.sleep(0.1) # wait 100ms to give device time to start running self._rxThread.start() @@ -88,6 +89,9 @@ class StreamInterface(MeshInterface): if self.stream: # ignore writes when stream is closed self.stream.write(b) self.stream.flush() + # we sleep here to give the TBeam a chance to work + if not self.noProto: + time.sleep(0.1) def _readBytes(self, length): """Read an array of bytes from our stream"""