Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use RFC2217 network serial ports with Twisted Python?

Is there a way to connect to an RFC2217 networked serial port with Twisted Python?

Pyserial seems to support it via the serial.serial_for_url("rfc2217://...") function. And they indicate that twisted uses pyserial for managing serial connections, however twisted.internet.serialport.SerialPort seems to expect a port name or number which suggests it is just passing this to the serial.Serial constructor.

I can use socat to create a PTY externally and pass the dev name to twisted which works fine, but I was wondering if I can bypass this step by using the pyserial support directly.

socat PTY,link=/dev/myport TCP:192.168.1.222:9001

Edit: The pyserial faq suggests this modification for instantiating serial objects:

try:
    s = serial.serial_for_url(...)
except AttributeError:
    s = serial.Serial(...)

Not sure if this helps though...

like image 630
Peter Gibson Avatar asked May 12 '15 05:05

Peter Gibson


1 Answers

I have come to the conclusion that using Pyserial's RFC2217 support with Twisted Python is non-trivial. Pyserial's implementation of RFC2217, besides being currently experimental, uses threads to manage the socket connection which they state as being a problem for select based applications:

The current implementation starts a thread that keeps reading from the (internal) socket. The thread is managed automatically by the rfc2217.Serial port object on open()/close(). However it may be a problem for user applications that like to use select instead of threads.

It is fairly straight forward to subclass t.i.serialport.SerialPort and overwrite the _serialFactory method (which creates the pyserial object to be used for accessing the serial port)

class SerialPort(serialport.SerialPort):
    def _serialFactory(self, dev, *args, **kwargs):
        " pyserial recommends the following for supporting serial urls "
        try:
            return serial.serial_for_url(dev)
        except AttributeError:
            return serial.Serial(dev, *args, **kwargs)

However, the resulting object lacks a file descriptor and so the fileno() method (used internally by t.i._posixserialport) throws an exception.

--- <exception caught here> ---
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/twisted/internet/base.py", line 1204, in mainLoop
    self.doIteration(t)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/twisted/internet/selectreactor.py", line 105, in doSelect
    [], timeout)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/twisted/internet/_posixserialport.py", line 48, in fileno
    return self._serial.fd
exceptions.AttributeError: 'Serial' object has no attribute 'fd'

The current workarounds are to either use socat as described in the question, or for the network serial server I'm using (Brainboxes ES-842) you can configure it in "Raw TCP" mode instead of "Telnet/RFC2217" mode and just use your existing protocol over a TCP connection (as long as you're not depending on flow control or other serial control lines and can use a predefined fixed baud rate).

like image 183
Peter Gibson Avatar answered Nov 15 '22 10:11

Peter Gibson