Linux下的虚拟串行通信

17 投票
4 回答
11433 浏览
提问于 2025-04-15 20:45

我有一个应用程序,想要模拟一个设备和“调制解调器”之间的连接。这个设备会连接到一个串口,通过这个串口和软件调制解调器进行通信。

为了测试,我希望能使用一个虚拟的软件设备来测试发送和接收数据。

下面是一个示例的Python代码

device = Device()
modem  = Modem()
device.connect(modem)

device.write("Hello")
modem_reply = device.read()

在我的最终应用中,我只需要传递 /dev/ttyS1 或 COM1 之类的路径供应用使用。但我该如何在软件中实现这个呢?我正在使用Linux,而应用是用Python编写的。

我尝试过创建一个FIFO(mkfifo ~/my_fifo),这个方法是有效的,但我需要一个FIFO用于写入,一个用于读取。我想要的是打开~/my_fake_serial_port,然后可以在里面读写。

我也试过pty模块,但也没能成功。我可以通过pty.openpty()获得一个主文件描述符和一个从文件描述符,但尝试读取或写入时总是出现IOError Bad File Descriptor的错误信息。

更新

有评论提醒我去看一个StackOverflow的问题 在Linux中有没有类似COM0COM的程序?,这个问题提到使用socat来设置一个虚拟串口连接。我是这样使用的:

socat PTY,link=$HOME/COM1 PTY,link=$HOME/COM2

感谢其他人提供的宝贵信息。我选择接受Vinay Sajips的回答,因为这是我在看到socat建议之前就已经尝试过的解决方案。看起来效果还不错。

4 个回答

1

这里有一个用Python写的pts-emulated(caf的)串口通信的版本:

from serial import Serial

driver = MyDriver()  # what I want to test
peer = serial.Serial()
driver.port.fd, peer.fd = posix.openpty()
driver.port._reconfigurePort()
peer.setTimeout(timeout=0.1)
peer._reconfigurePort()
driver.start()

# peer.write("something")
# driver.get_data_from_serial()

这个方法比模拟串口的方式有一些好处,主要是它使用了真实的串口代码,并且能测试一些串口的特性。

如果你想测试打开串口的功能,可以把主设备和从设备的位置调换,然后用os.ttyname(salve_fd)来作为串口的名称。不过,我不能保证调换主从设备会有什么副作用。最明显的一点是,你可以关闭并重新打开从设备,但如果你关闭了主设备,从设备也会随之失效。

如果你的测试代码在同一个进程中运行,这个方法效果很好。不过,我还没有解决在多个或独立进程中运行时可能出现的问题。

5

你现在的方向是对的,关于伪终端的使用。要做到这一点,你的模拟软件设备首先需要打开一个伪终端主设备——这就像是一个文件通道,它会用来和你正在测试的串口软件进行读写交流。接下来,它需要给伪终端从设备授权并解锁,然后获取从设备的名称。之后,它应该把这个从设备的名称打印出来,这样你就可以告诉其他软件去把它当作串口来打开(也就是说,那个软件会打开像 /dev/pts/0 这样的名称,而不是 /dev/ttyS1)。

然后,模拟软件就可以在伪终端的主设备那边进行读写操作。在C语言中,它的写法大概是这样的:

#define _XOPEN_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>

int main(int argc, char *argv[])
{
    int pt;

    pt = open("/dev/ptmx", O_RDWR | O_NOCTTY);
    if (pt < 0)
    {
        perror("open /dev/ptmx");
        return 1;
    }

    grantpt(pt);
    unlockpt(pt);

    fprintf(stderr, "Slave device: %s\n", ptsname(pt));

    /* Now start pretending to be a modem, reading and writing "pt" */
    /* ... */
    return 0;
}

希望这个内容转换成Python会比较简单。

11

最好使用 pyserial 来和串口进行通信。你可以创建一个模拟版本的 serial.Serial 类,这个类可以实现 readreadlinewrite 以及你需要的其他方法。

撰写回答