Python中的虚拟串行设备?

56

我知道可以使用例如pySerial来与串行设备通信,但是如果我现在没有设备却需要编写一个客户端,我该怎么办呢?如何在Python中编写一个“虚拟串行设备”,并让pySerial与其通信,就像运行本地Web服务器一样?也许我只是搜索得不好,但我一直找不到关于这个主题的任何信息。

6个回答

56
这是我目前为止做过并且有效的内容:
import os, pty, serial

master, slave = pty.openpty()
s_name = os.ttyname(slave)

ser = serial.Serial(s_name)

# To Write to the device
ser.write('Your text')

# To read from the device
os.read(master,1000)

如果您创建更多的虚拟端口,则即使它们具有相同的名称,不同的主机也会获得不同的文件描述符,从而避免了问题。


10
这将无法在Windows上运行,因为Windows缺少pty所需的termios模块。 - OYRM
1
我似乎可以很好地从虚拟设备中读取(即我有另一个程序在端点s_name写入设备),但是每当我发出ser.write("...")时,该文本只会在下一次os.read(master,1000)时被回显,无论是否连接到端口,而且端口的另一端似乎没有收到数据。 - Ponkadoodle
1
当我在Ubuntu 14.04上运行您的代码时,出现以下错误: File "<stdin>", line 1, in <module> File "/usr/local/lib/python2.7/dist-packages/serial/serialutil.py", line 180, in __init__ self.open() File "/usr/local/lib/python2.7/dist-packages/serial/serialposix.py", line 311, in open self._update_dtr_state() File "/usr/local/lib/python2.7/dist-packages/serial/serialposix.py", line 605, in _update_dtr_state fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_DTR_str) IOError: [Errno 22] Invalid argument - Samuel Góngora
1
@SamuelGóngora 还可以看看这个:https://dev59.com/wlsW5IYBdhLWcg3wk34q,我认为这可能是问题更准确的原因。 - Patrick Steadman
3
pyserial的单元测试有一组很好的示例: https://github.com/pyserial/pyserial/blob/7bd427087857ba474180058b727578ca4cec5e2e/test/test_pty.py - Matt
显示剩余4条评论

10
我能够使用以下代码模拟任意串口 ./foo

SerialEmulator.py

import os, subprocess, serial, time

# this script lets you emulate a serial device
# the client program should use the serial port file specifed by client_port

# if the port is a location that the user can't access (ex: /dev/ttyUSB0 often),
# sudo is required

class SerialEmulator(object):
    def __init__(self, device_port='./ttydevice', client_port='./ttyclient'):
        self.device_port = device_port
        self.client_port = client_port
        cmd=['/usr/bin/socat','-d','-d','PTY,link=%s,raw,echo=0' %
                self.device_port, 'PTY,link=%s,raw,echo=0' % self.client_port]
        self.proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        time.sleep(1)
        self.serial = serial.Serial(self.device_port, 9600, rtscts=True, dsrdtr=True)
        self.err = ''
        self.out = ''

    def write(self, out):
        self.serial.write(out)

    def read(self):
        line = ''
        while self.serial.inWaiting() > 0:
            line += self.serial.read(1)
        print line

    def __del__(self):
        self.stop()

    def stop(self):
        self.proc.kill()
        self.out, self.err = self.proc.communicate()

需要安装socat (sudo apt-get install socat) 和 pyserial python包 (pip install pyserial)。

打开Python解释器并导入SerialEmulator:

>>> from SerialEmulator import SerialEmulator
>>> emulator = SerialEmulator('./ttydevice','./ttyclient') 
>>> emulator.write('foo')
>>> emulator.read()

你的客户端程序可以使用pyserial将./ttyclient封装起来,创建虚拟串口。如果无法修改客户端代码,则可以将client_port设置为/dev/ttyUSB0或类似的内容,但可能需要sudo权限。此外还要注意这个问题:Pyserial与虚拟端口不兼容

你在GitHub上有这段代码吗?我为什么需要'ttydevice'和'ttyclient'?我想要一个Python程序(设备仿真器),我将其作为单独的进程运行。它将创建'./ttymydevice',并像真正的设备一样响应我的串行命令。 - Valentyn

10
如果您正在运行Linux操作系统,您可以使用“socat”命令进行此操作,方法如下:socat
socat -d -d pty,raw,echo=0 pty,raw,echo=0

当命令运行时,它将告诉您它创建了哪些串行端口。在我的计算机上,它看起来像这样:
2014/04/23 15:47:49 socat[31711] N PTY is /dev/pts/12
2014/04/23 15:47:49 socat[31711] N PTY is /dev/pts/13
2014/04/23 15:47:49 socat[31711] N starting data transfer loop with FDs [3,3] and [5,5]

现在我可以向/dev/pts/13写入并从/dev/pts/12接收,反之亦然。

6

如果你在Windows系统上,可以使用类似com0com的工具来设置虚拟串口,然后进行开发。这样可能会更加方便。


有没有免费的com0com替代品(带API),我可以在我的商业软件中原样使用?我只需要用户拥有虚拟串口。 - mrid

5

4
这有点取决于你现在想要实现什么...
你可以将对串行端口的访问封装在一个类中,并编写一个实现以使用套接字 I/O 或文件 I/O。然后编写串行 I/O 类以使用相同的接口,并在设备可用时插入它。(这实际上是一种测试功能而不需要外部硬件的好设计。)
或者,如果您要使用串行端口进行命令行界面,可以使用 stdin/stdout。
还有另一个关于 linux 虚拟串行设备 的答案。

是的,请编写一个“虚拟串行设备”类,该类复制了pySerial接口。然后,您的代码可以交替使用您的“虚拟设备”类或真实的pySerial接口。 - Craig McQueen
这是一个很好的实现,因为它相当容易实现,并且可以在所有平台上运行! - Pithikos
1
抱歉更新这篇旧帖,但您能否再详细解释一下?这对我非常有帮助。谢谢。 - rowman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接