Bridge Serial-Ports Over Network

Bridge Serial-Ports over network

One side of the tcp connection needs to be listening on the port (the first one you launch), and the second side connects to it.

For the first side do:

socat tcp-listen:8080 pty,link=/dev/virtualport0

And for the second side do:

socat pty,link=/dev/virtualcom0 tcp:IP-of-other-machine:8080

Forget the netcat, you do not need it.

Create a virtual serial port connection over TCP

Try socat. Possible scenario:

socat  pty,link=/dev/virtualcom0,raw  tcp:192.168.254.254:8080&

socat creates TCP connection to 192.168.254.254:8080, so that everything, that will be written to /dev/virtualcom0 will be forwarded to 192.168.254.254:8080 and vice versa.

Another approach would be to use RFC2217 via ser2net on Linux sever side and RFC2217 driver on Windows side (for example http://www.hw-group.com/products/hw_vsp/index_en.html single port version). You can also try to get http://pyserial.sourceforge.net/ to work with ser2net.

Virtual Serial Port for Linux

You can use a pty ("pseudo-teletype", where a serial port is a "real teletype") for this. From one end, open /dev/ptyp5, and then attach your program to /dev/ttyp5; ttyp5 will act just like a serial port, but will send/receive everything it does via /dev/ptyp5.

If you really need it to talk to a file called /dev/ttys2, then simply move your old /dev/ttys2 out of the way and make a symlink from ptyp5 to ttys2.

Of course you can use some number other than ptyp5. Perhaps pick one with a high number to avoid duplicates, since all your login terminals will also be using ptys.

Wikipedia has more about ptys: http://en.wikipedia.org/wiki/Pseudo_terminal

How to forward serial port data to a multiclient socket server in Python?

You need to implement some logic for when a network client disconnects.

You know a client has disconnected because you receive an empty response (b'') from the socket. You're receiving data from network clients in NetToSerial, here:

    def run(self):
try:
while True:
data = self._socket.recv(1024)
serial_worker.write(data)
except (ConnectionAbortedError, ConnectionResetError):
print("NetToSerial client disconnection")
return

You need to check the value of data, and if it's empty implement your disconnect logic:

  • Close the associated socket.
  • Exit the thread.

That might look like:

class NetToSerial(Thread):
"""socket->serial"""

serial_worker: serial.threaded.ReaderThread

def __init__(self, client_socket):
Thread.__init__(self)
self._socket = client_socket

def run(self):
try:
while True:
data = self._socket.recv(1024)
if not data:
break
serial_worker.write(data)
except (ConnectionAbortedError, ConnectionResetError):
print("NetToSerial client disconnection")
return
finally:
self._socket.close()

But that's only half the solution, because you're writing to this socket in your SerialToNet class. You need to remove the socket from SerialToNet sockets array. You can have the class remove the socket in response to an exception when writing, like this:

class SerialToNet(serial.threaded.Protocol):
"""serial->socket"""

def __init__(self):
self.sockets: list[socket.socket] = []

def __call__(self):
return self

def data_received(self, data):
"""Forward data from Serial to IP client Sockets"""
for socket in self.sockets[:]:
try:
socket.sendall(data)
except OSError:
self.sockets.remove(socket)

Note that because it's not possible to remove an item from a list over which you're currently iterating, we are iterating over a copy of self.sockets in the above code. This means we're free to remove sockets from self.sockets from inside the loop.


With the above changes I believe your code will operate as you intend.

Not directly related to your question, but I'd like to make a comment about your code: as written, it allows multiple network clients to write to the serial port at the same time. That seems like a recipe for disaster and I cannot think of any situation in which that would make sense. You may want to reconsider that aspect of your code.



Related Topics



Leave a reply



Submit