在Python虚拟机中实现串口通信配置,核心思路是通过虚拟串口对(如com0com)创建物理链路,再用pyserial库建立读写通道,整个过程只需三步即可跑通。
为什么要在虚拟机里配串口
很多嵌入式开发者在Windows主机上写完代码,却发现程序在真实硬件上跑不通,原因往往出在串口环境差异上,虚拟机里的串口通信配置,本质上是把主机的物理串口或虚拟串口映射给虚拟机系统,让Linux或Windows虚拟机里的程序能像操作真实设备一样收发数据。
常见的使用场景包括:
- 在Ubuntu虚拟机中调试STM32、ESP32等微控制器程序
- 用VMware或VirtualBox运行老式工控软件,需要访问COM口
- 测试串口通信协议时,不想反复插拔真实硬件
- 多虚拟机之间通过虚拟串口互联,模拟设备组网
行业共识认为,串口通信配置的难点不在代码本身,而在链路建立环节,网上很多教程直接讲pyserial的API,却忽略了虚拟机串口映射这一步,导致读者照做后依然通信失败。
虚拟机串口映射的两种典型方式
物理串口直通映射
如果你的宿主机有真实串口设备(比如USB转串口模块),可以直接把设备映射进虚拟机。
VMware Fusion/Workstation的操作路径:
- 关闭虚拟机,在虚拟机设置中选择“添加硬件”
- 选择“串行端口”,在连接模式中选择“使用物理串口”
- 指定物理端口(如COM3或/dev/ttyUSB0)
- 启动虚拟机,在客户机中查看串口设备是否识别
VirtualBox的操作路径:
- 在虚拟机设置中选择“串口”
- 勾选“启用串口”
- 端口号选择COM1或COM2,端口模式选择“主机设备”
- 路径填入主机的物理串口路径
需要注意的是,直通映射对USB转串口模块的兼容性参差不齐,如果映射后虚拟机里看不到设备节点,优先检查驱动是否安装,其次尝试更换USB口。
虚拟串口对互联
当没有物理硬件时,可以用虚拟串口对软件(Windows端用com0com,Linux端用socat)创建一对互联的虚拟串口。
实际操作步骤:
Windows主机创建虚拟串口对:
# 安装com0com后,用管理员权限执行 cd C:Program Files (x86)com0com setup.exe
在GUI界面中点击“Add Pair”,会自动生成COM3和COM4这一对虚拟串口,COM3留给主机程序,COM4映射给虚拟机。
Linux主机用socat创建虚拟串口对:
sudo apt install socat sudo socat -d -d pty,raw,echo=0 pty,raw,echo=0
执行后终端会输出两个/dev/pts/N节点,这两个节点就是互联的虚拟串口。
把虚拟串口映射进虚拟机:
以VMware为例,在虚拟机串口设置中选择“使用命名管道”,写入Windows主机虚拟串口的命名管道路径,如\.pipecom_4,这里的关键点是,命名管道路径需要和com0com中配置的管道名称保持一致。
Python代码层面的串口通信配置
链路建立后,Python代码其实与操作真实串口没有本质区别,这里给出一个标准的通信配置模板:
import serial
import time
# 初始化串口参数
ser = serial.Serial(
port='/dev/ttyS1', # Linux虚拟机中通常是ttyS1,Windows中为COM1
baudrate=115200, # 波特率
bytesize=serial.EIGHTBITS, # 8位数据位
parity=serial.PARITY_NONE, # 无校验位
stopbits=serial.STOPBITS_ONE, # 1位停止位
timeout=1 # 读超时1秒
)
# 发送数据
ser.write(b'ATrn')
# 读取响应
response = ser.readline()
print(response.decode('utf-8', errors='ignore'))
# 完整通信过程
def send_and_receive(command):
ser.reset_input_buffer()
ser.write(command.encode())
time.sleep(0.1)
result = ser.read_all()
return result
# 使用完毕后关闭串口
ser.close()
关键参数说明:
| 参数 | 常用值 | 说明 |
|---|---|---|
| port | /dev/ttyS1, COM3 | 虚拟机中的设备节点路径 |
| baudrate | 9600 / 115200 | 波特率需与对端设备匹配 |
| timeout | 1 | 避免读取时永久阻塞 |
| write_timeout | 2 | 写入超时,防止卡死 |
实测中,多数连接异常都源于timeout设置过短,比如对端设备响应需要2秒,而你设了0.5秒超时,程序就会误判为通信失败,建议初始调试时把timeout设为3秒,稳定后再缩短。
虚拟机串口通信的坑与排查方法
坑一:设备节点存在但打不开
在Linux虚拟机中执行ls /dev/ttyS能看到设备节点,但程序打开时报Permission denied,这是权限问题。
解决方案:
# 把当前用户加入dialout组 sudo usermod -a -G dialout $USER # 或者临时授权 sudo chmod 666 /dev/ttyS1
坑二:虚拟串口通了但数据乱码
数据能收发,但内容无法解析,多数情况下是波特率不匹配,或者是数据位、停止位设置错误,如果硬件端是7位数据位加偶校验,而Python端写了8位无校验,必然乱码。
坑三:监听不到串口活动
可以用Python的serial.tools.list_ports模块列出虚拟机识别到的所有串口:
from serial.tools import list_ports
ports = list_ports.comports()
for port in ports:
print(port.device, port.description)
如果列出为空,说明虚拟机层的串口添加有问题,与Python代码无关,回头检查虚拟机设置。
python虚拟机串口通信配置的进阶技巧
使用threading实现非阻塞收发
实际项目中,串口通信往往是边接收边处理,用单线程循环容易卡界面或漏数据。
import threading
class SerialReader(threading.Thread):
def __init__(self, port, baud):
super().__init__()
self.daemon = True
self.ser = serial.Serial(port, baud, timeout=1)
self.running = True
def run(self):
while self.running:
if self.ser.in_waiting:
data = self.ser.read(self.ser.in_waiting)
print(f"收到: {data.hex()}")
time.sleep(0.01)
def stop(self):
self.running = False
self.ser.close()
自动化配置虚拟串口参数
在QEMU虚拟机中,串口参数可以写在启动脚本里:
qemu-system-x86_64
-chardev socket,id=chrtty,path=/tmp/ttyS1,server=on,wait=off
-serial chardev:chrtty
这种方式的好处是,主机程序直接往/tmp/ttyS1写数据,虚拟机内的Python程序从相同路径读数据,不依赖GUI设置。
虚拟机串口调试中的常见疑问
为什么虚拟机串口比物理机慢
虚拟串口数据经过宿主机的管道转发,延迟通常在毫秒级,对于115200波特率的通信来说影响不大,但如果你做的是高实时性通信(比如毫秒级握手协议),建议用直通模式或者采用QEMU的socket方式减少一层转发。
VMware和VirtualBox在串口配置上谁更简单
VMware的图形化界面更直观,支持命名管道和物理串口直连,VirtualBox在Windows主机上偶尔会遇到驱动不兼容的问题,Linux主机下,VirtualBox的串口映射到/dev/ttyS0往往需要手动添加vboxusers用户组权限,步骤略多。
虚拟机串口通信测试软件推荐
除了用Python代码测试,也可以用socat直接验证链路是否畅通,在主机端执行:
socat - /dev/ttyS1
虚拟机的Python程序发送数据,主机端终端能显示出来,就说明链路完全正常,此时再排查Python代码参数问题。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/755076.html





