mirror of
https://github.com/meshtastic/python.git
synced 2026-03-02 05:30:10 -05:00
WIP - adding TCP client, still need to use recv/send instead of read/write
This commit is contained in:
@@ -47,12 +47,13 @@ def onConnection(interface, topic=pub.AUTO_TOPIC): # called when we (re)connect
|
||||
pub.subscribe(onReceive, "meshtastic.receive")
|
||||
pub.subscribe(onConnection, "meshtastic.connection.established")
|
||||
# By default will try to find a meshtastic device, otherwise provide a device path like /dev/ttyUSB0
|
||||
interface = meshtastic.StreamInterface()
|
||||
interface = meshtastic.SerialInterface()
|
||||
|
||||
```
|
||||
|
||||
"""
|
||||
|
||||
import socket
|
||||
import pygatt
|
||||
import google.protobuf.json_format
|
||||
import serial
|
||||
@@ -417,9 +418,8 @@ class BLEInterface(MeshInterface):
|
||||
class StreamInterface(MeshInterface):
|
||||
"""Interface class for meshtastic devices over a stream link (serial, TCP, etc)"""
|
||||
|
||||
def __init__(self, devPath=None, debugOut=None, noProto=False, connectNow=True):
|
||||
"""Constructor, opens a connection to a specified serial port, or if unspecified try to
|
||||
find one Meshtastic device by probing
|
||||
def __init__(self, debugOut=None, noProto=False, connectNow=True):
|
||||
"""Constructor, opens a connection to self.stream
|
||||
|
||||
Keyword Arguments:
|
||||
devPath {string} -- A filepath to a device, i.e. /dev/ttyUSB0 (default: {None})
|
||||
@@ -430,35 +430,12 @@ class StreamInterface(MeshInterface):
|
||||
Exception: [description]
|
||||
"""
|
||||
|
||||
if devPath is None:
|
||||
ports = util.findPorts()
|
||||
if len(ports) == 0:
|
||||
raise Exception("No Meshtastic devices detected")
|
||||
elif len(ports) > 1:
|
||||
raise Exception(
|
||||
f"Multiple ports detected, you must specify a device, such as {ports[0].device}")
|
||||
else:
|
||||
devPath = ports[0]
|
||||
|
||||
logging.debug(f"Connecting to {devPath}")
|
||||
self.devPath = devPath
|
||||
if not hasattr(self, 'stream'):
|
||||
raise Exception(
|
||||
"StreamInterface is now abstract (to update existing code create SerialInterface instead)")
|
||||
self._rxBuf = bytes() # empty
|
||||
self._wantExit = False
|
||||
|
||||
# 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)
|
||||
|
||||
# rts=False Needed to prevent TBEAMs resetting on OSX, because rts is connected to reset
|
||||
self.stream.port = devPath
|
||||
# OS-X seems to have a bug in its serial driver. 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). Since it is going to drive it anyways we want to make
|
||||
# sure it is driven low, so that the TBEAM won't reset
|
||||
if platform.system() == 'Darwin':
|
||||
self.stream.rts = False
|
||||
self.stream.open()
|
||||
|
||||
self._rxThread = threading.Thread(target=self.__reader, args=())
|
||||
|
||||
MeshInterface.__init__(self, debugOut=debugOut, noProto=noProto)
|
||||
@@ -481,11 +458,19 @@ class StreamInterface(MeshInterface):
|
||||
|
||||
self._rxThread.start()
|
||||
|
||||
def _disconnected(self):
|
||||
"""We override the superclass implementation to close our port"""
|
||||
MeshInterface._disconnected(self)
|
||||
|
||||
logging.debug("Closing our port")
|
||||
self.stream.close()
|
||||
|
||||
def _sendToRadio(self, toRadio):
|
||||
"""Send a ToRadio protobuf to the device"""
|
||||
logging.debug(f"Sending: {toRadio}")
|
||||
b = toRadio.SerializeToString()
|
||||
bufLen = len(b)
|
||||
# We convert into a string, because the TCP code doesn't work with byte arrays
|
||||
header = bytes([START1, START2, (bufLen >> 8) & 0xff, bufLen & 0xff])
|
||||
self.stream.write(header)
|
||||
self.stream.write(b)
|
||||
@@ -543,14 +528,82 @@ class StreamInterface(MeshInterface):
|
||||
traceback.print_exc()
|
||||
self._rxBuf = empty
|
||||
else:
|
||||
# logging.debug(f"timeout on {self.devPath}")
|
||||
# logging.debug(f"timeout")
|
||||
pass
|
||||
except serial.SerialException as ex:
|
||||
logging.warn(
|
||||
f"Meshtastic serial port disconnected, disconnecting... {ex}")
|
||||
finally:
|
||||
logging.debug("reader is exiting")
|
||||
if platform.system() == 'Darwin':
|
||||
self.stream.rts = True # Return RTS high, so that the reset button still works
|
||||
self.stream.close()
|
||||
self._disconnected()
|
||||
|
||||
|
||||
class SerialInterface(StreamInterface):
|
||||
"""Interface class for meshtastic devices over a serial link"""
|
||||
|
||||
def __init__(self, devPath=None, debugOut=None, noProto=False, connectNow=True):
|
||||
"""Constructor, opens a connection to a specified serial port, or if unspecified try to
|
||||
find one Meshtastic device by probing
|
||||
|
||||
Keyword Arguments:
|
||||
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})
|
||||
"""
|
||||
|
||||
if devPath is None:
|
||||
ports = util.findPorts()
|
||||
if len(ports) == 0:
|
||||
raise Exception("No Meshtastic devices detected")
|
||||
elif len(ports) > 1:
|
||||
raise Exception(
|
||||
f"Multiple ports detected, you must specify a device, such as {ports[0].device}")
|
||||
else:
|
||||
devPath = ports[0]
|
||||
|
||||
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)
|
||||
|
||||
# rts=False Needed to prevent TBEAMs resetting on OSX, because rts is connected to reset
|
||||
self.stream.port = devPath
|
||||
# OS-X seems to have a bug in its serial driver. 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). Since it is going to drive it anyways we want to make
|
||||
# sure it is driven low, so that the TBEAM won't reset
|
||||
if platform.system() == 'Darwin':
|
||||
self.stream.rts = False
|
||||
self.stream.open()
|
||||
|
||||
StreamInterface.__init__(
|
||||
self, debugOut=debugOut, noProto=noProto, connectNow=connectNow)
|
||||
|
||||
def _disconnected(self):
|
||||
"""We override the superclass implementation to close our port"""
|
||||
|
||||
if platform.system() == 'Darwin':
|
||||
self.stream.rts = True # Return RTS high, so that the reset button still works
|
||||
|
||||
StreamInterface._disconnected(self)
|
||||
|
||||
|
||||
class TCPInterface(StreamInterface):
|
||||
"""Interface class for meshtastic devices over a TCP link"""
|
||||
|
||||
def __init__(self, hostname, debugOut=None, noProto=False, connectNow=True, portNumber=4403):
|
||||
"""Constructor, opens a connection to a specified IP address/hostname
|
||||
|
||||
Keyword Arguments:
|
||||
hostname {string} -- Hostname/IP address of the device to connect to
|
||||
"""
|
||||
|
||||
logging.debug(f"Connecting to {hostname}")
|
||||
|
||||
server_address = (hostname, portNumber)
|
||||
sock = socket.create_connection(server_address)
|
||||
|
||||
self.stream = sock.makefile('rw')
|
||||
|
||||
StreamInterface.__init__(
|
||||
self, debugOut=debugOut, noProto=noProto, connectNow=connectNow)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!python3
|
||||
|
||||
import argparse
|
||||
from . import StreamInterface, BLEInterface, test
|
||||
from . import SerialInterface, TCPInterface, BLEInterface, test
|
||||
import logging
|
||||
import sys
|
||||
from pubsub import pub
|
||||
@@ -162,10 +162,15 @@ def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument(
|
||||
"--device",
|
||||
"--port",
|
||||
help="The port the Meshtastic device is connected to, i.e. /dev/ttyUSB0. If unspecified, we'll try to find it.",
|
||||
default=None)
|
||||
|
||||
parser.add_argument(
|
||||
"--host",
|
||||
help="The hostname/ipaddr of the device to connect to (over TCP)",
|
||||
default=None)
|
||||
|
||||
parser.add_argument(
|
||||
"--seriallog",
|
||||
help="Log device serial output to either 'stdout', 'none' or a filename to append to. Defaults to stdout.",
|
||||
@@ -205,8 +210,8 @@ def main():
|
||||
parser.add_argument("--test", help="Run stress test against all connected Meshtastic devices",
|
||||
action="store_true")
|
||||
|
||||
parser.add_argument("--ble", help="hack for testing BLE code (BLE is not yet supported for this tool)",
|
||||
action="store_true")
|
||||
parser.add_argument("--ble", help="BLE mac address to connect to (BLE is not yet supported for this tool)",
|
||||
default=None)
|
||||
|
||||
parser.add_argument("--noproto", help="Don't start the API, just function as a dumb serial terminal.",
|
||||
action="store_true")
|
||||
@@ -233,10 +238,13 @@ def main():
|
||||
|
||||
subscribe()
|
||||
if args.ble:
|
||||
client = BLEInterface(args.device, debugOut=logfile)
|
||||
client = BLEInterface(args.ble, debugOut=logfile)
|
||||
elif args.host:
|
||||
client = TCPInterface(
|
||||
args.host, debugOut=logfile, noProto=args.noproto)
|
||||
else:
|
||||
client = StreamInterface(
|
||||
args.device, debugOut=logfile, noProto=args.noproto)
|
||||
client = SerialInterface(
|
||||
args.port, debugOut=logfile, noProto=args.noproto)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import logging
|
||||
from . import util
|
||||
from . import StreamInterface, BROADCAST_NUM
|
||||
from . import SerialInterface, BROADCAST_NUM
|
||||
from pubsub import pub
|
||||
import time
|
||||
import sys
|
||||
@@ -128,7 +128,7 @@ def testAll():
|
||||
pub.subscribe(onConnection, "meshtastic.connection")
|
||||
pub.subscribe(onReceive, "meshtastic.receive")
|
||||
global interfaces
|
||||
interfaces = list(map(lambda port: StreamInterface(
|
||||
interfaces = list(map(lambda port: SerialInterface(
|
||||
port, debugOut=openDebugLog(port), connectNow=False), ports))
|
||||
for i in interfaces:
|
||||
i.connect()
|
||||
|
||||
Reference in New Issue
Block a user